Skip to content

Commit 5390f8e

Browse files
committed
update 4.1 4.2 add 4.3
1 parent 968b9f4 commit 5390f8e

File tree

1 file changed

+90
-10
lines changed

1 file changed

+90
-10
lines changed

04_Function.md

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
索引
44

55
1.[函数的作用](#函数的作用)
6-
2.[函数的使用](#函数的使用)
7-
3.[作用域与可访问域](#作用域与可访问域)
6+
2.[函数的交互](#函数的交互)
7+
3.[函数的调用](#函数的调用)
88
4.[拓展_高阶函数](#拓展_高阶函数)
99

1010
## 函数的作用
11-
我们时常会听到,某个成熟的软件项目代码行数达到了上百万的级别,如果将这些代码全都只写在一个源文件之中,那每次修改的时候光打开这个文件就需要消耗相当的时间。抛开机器的性能来说,还会出现很多的问题,比如:
11+
我们时常会听到,某个成熟的软件项目代码行数达到了上百万的级别,如果这些代码全都在一个源文件之中,那人们每次对其进行编辑的时候光打开就需要消耗相当的时间。除了这个问题之外,还会出现其他更多的一些问题,比如:
1212
+ 所有的逻辑代码混合在一起,可读性极差,每次阅读代码都会十分痛苦;
1313
+ 各种各样的计算都需要用到大量的临时变量来存储,使得变量命名较为容易发生混乱;
1414
+ 如果只有一份文件,那么多人进行协同开发听起来更像是天方夜谭;
1515
+ 在进行debug调试的时候,如此巨大的代码量会让人无从下手。
1616

17-
所以,一种对代码进行抽象的封装技术便应运而生了--函数。
17+
所以,一种对代码进行抽象的封装技术便不可或缺了--函数。
1818

19-
首先,如果让我们打印一首辛弃疾的词,我们知道了前面的内容之后可以这样
19+
首先,如果让我们打印一首辛弃疾的词,我们在学习完前面的内容之后可以这样
2020

2121
~~~python
2222
print "丑奴儿·书博山道中壁"
@@ -26,9 +26,9 @@ print "而今识尽愁滋味,欲说还休。"
2626
print "欲说还休,却道天凉好个秋。"
2727
~~~
2828

29-
那么,假如让我们打印两次的话,我们对这5行代码进行Ctrl+c, Ctrl+v个两次也是完成了目标,到这里我们或许会感觉到有些地方不对劲,上面这5行代码是各个独立的个体,写完之后,它们仍是互相独立,无任何关联。这时候,如果说一个工程成千上万个小功能每次都是从头来过,这明显也是不可接受的
29+
那么,假如让我们打印两次的话,为了完成预期的目标我们可以对这5行代码进行Ctrl+c、Ctrl+v,但这样做的话或许会发现有的地方不对劲,上面这5行代码之间是各个独立的个体,写完之后,它们仍是互相独立,无任何关联,也就无法在第二个需要这段代码的地方方便地进行复用。如果说,一个工程中成千上万个重复的小功能每次编写都是重新地写代码、复制代码,这显然是不可接受的
3030

31-
为了克服上述的困难,让我们来对这5行代码进行抽象,将其封装成一个函数,然后对该函数进行调用。这样一来,我们不仅实现了的目的,并且,还可以看出来该程序对这整个代码块执行了2次,提高了代码的**可读性**
31+
为了克服上述的困难,我们可以对这5行代码进行抽象,将其封装成一个函数,然后对该函数进行调用。这样一来,我们就实现了对代码块复用,并且,还可以看出来该程序对这整个代码块执行了2次,提高了代码的**可读性**
3232

3333
~~~python
3434
def print_XinQiji_SongPoems():
@@ -42,8 +42,8 @@ print_XinQiji_SongPoems()
4242
print_XinQiji_SongPoems()
4343
~~~
4444

45-
## 函数的使用
46-
如果函数只是简单地对多行代码进行封装的话,那还解决不了我们一开始提出的问题,我们还需要与函数进行交互,这样才能够进行数据交换,从而进行更加复杂的数据操作
45+
## 函数的交互
46+
如果函数只是简单地对多行代码进行封装的话,那还不足以解决我们一开始提出的诸多问题,我们还需要与函数进行交互,这样才能够进行数据交换,从而进行一系列更加复杂的数据操作
4747

4848
一般情况下,函数有任意数量的输入,称为函数参数,和一个输出,称为返回值。在进行函数调用的时候,外部通过传递参数给函数,函数执行完毕后返回一个返回值,至此完成函数的调用。
4949

@@ -69,14 +69,94 @@ sum_of_square(3, 4)
6969
# 25
7070
~~~
7171

72+
上述代码定义了一个sum_of_square函数并对其进行了调用,为了方便我们的理解,我们尝试使用一种“代换模型”来看看上述代码在去掉函数调用的结构是长什么样的,并且,还会去掉用于帮助理解的打印语句。
73+
74+
~~~python
75+
76+
77+
# 原始
78+
sum_of_square(a, b)
79+
80+
# 第一次代换
81+
square(a) + square(b)
82+
83+
# 第二次代换
84+
a*a + b*b
85+
86+
# 把a=3和b=4代换进去得出结果
87+
# 9 + 16
88+
25
89+
~~~
90+
91+
需要特别说明的是,“代换模型”并不是实际计算机的工作方式,只是一种方便我们理解的模型,因为往往计算机会对代码进行复杂的优化步骤,经过优化之后的代码逻辑会与原先的有所不同,并且,我们会在后文讨论到代换还有一个地方无法直接代换--**递归函数**
92+
7293
``
7394
question:那么问题来了,第一节中的print_XinQiji_SongPoems函数并没有return一个值,这个属于什么情况?
7495
``
7596

76-
## 作用域与可访问域
7797

98+
## 函数的调用
99+
我们在执行单个python脚本文件的时候,是按照先后顺序执行每一行代码,这个文件则被看作是一个模块,其名字为不带拓展名的文件名。
100+
101+
在定义顶层变量的同时,这些变量组成了一个上下文环境(context),由于python语言是跟进缩进层次来分辨层次,这里的顶层指的是没有任何缩进的定义语句,处在函数之中定义的变量不属于顶层变量。
102+
103+
我们定义一个变量的时候,程序会在context创建这个变量,当需要使用这个变量的时候,则会在context中进行查找,如果查找不到,便会出现变量未定义的运行时错误,所以,变量一定要**先定义,后使用**
104+
105+
~~~python
106+
a = 1
107+
b = a
108+
~~~
109+
``
110+
question:那么问题来了,上述代码中的两个a具体是指什么,第一个a跟第二个a是一个含义吗?请自行理解下左值与右值。
111+
``
112+
113+
因为,函数也是一种对象,定义函数的时候就相当于定义了一个函数的变量,(注意这里用的是定义这个词,由于python是不用进行类型声明的,所以,声明与定义是同时发生的,声明了有这样的一个变量,这个变量的内容被定义成了什么。)我们无法在函数定义完成之前对其进行调用。
114+
115+
~~~python
116+
a()
117+
def a():
118+
pass
119+
120+
# NameError: name 'a' is not defined
121+
~~~
122+
123+
124+
接着,我们来考虑一种特殊一点的情况,函数间的互相调用:
125+
~~~python
126+
def a(a_num):
127+
if a_num == 3:
128+
return
129+
print a_num
130+
131+
b(a_num)
132+
133+
134+
def b(b_num):
135+
b_num += 1
136+
print b_num
137+
print '-' * 10
138+
139+
a(b_num)
140+
141+
a(1)
142+
143+
# 1
144+
# 2
145+
# ----------
146+
# 2
147+
# 3
148+
# ----------
149+
150+
# 接着还可以考虑下递归函数:函数在自己的定义过程中对自身进行了调用
151+
def c(c_num):
152+
print c_num
153+
c(c_num+1)
154+
c(c_num)
155+
~~~
78156

157+
在上述代码中,a函数中调用了b函数,而在这行代码之前b函数还未进行定义,似乎与我们之前所说的有点矛盾。但这里其实是与函数的定义机制有关,当我们定义一个函数的时候,python会先声明有这样一个函数,此时并未完成定义,而是会在我们调用的动作实际发生之前完成其定义,这种机制类似于前向声明,指的是声明标识符的时候没有给出完整的定义。所以我们前面在函数定义之前直接调用该函数会出现错误,因为这是实实在在的调用,而函数定义的代码中的进行对其它函数进行调用,实际上还未发生。
79158

159+
而这种递归或互相调用的函数,并不能跟之前的代换模型所说的那样直接把代码代换进去,因为这会无穷无尽,所以需要一点修改,在代换的时候也把参数也一起代换进去。
80160

81161

82162
## 拓展_高阶函数

0 commit comments

Comments
 (0)