33索引
44
551.[ 函数的作用] ( #函数的作用 )
6- 2.[ 函数的使用 ] ( #函数的使用 )
7- 3.[ 作用域与可访问域 ] ( #作用域与可访问域 )
6+ 2.[ 函数的交互 ] ( #函数的交互 )
7+ 3.[ 函数的调用 ] ( #函数的调用 )
884.[ 拓展_高阶函数] ( #拓展_高阶函数 )
99
1010## 函数的作用
11- 我们时常会听到,某个成熟的软件项目代码行数达到了上百万的级别,如果将这些代码全都只写在一个源文件之中,那每次修改的时候光打开这个文件就需要消耗相当的时间。抛开机器的性能来说,还会出现很多的问题 ,比如:
11+ 我们时常会听到,某个成熟的软件项目代码行数达到了上百万的级别,如果这些代码全都在一个源文件之中,那人们每次对其进行编辑的时候光打开就需要消耗相当的时间。除了这个问题之外,还会出现其他更多的一些问题 ,比如:
1212+ 所有的逻辑代码混合在一起,可读性极差,每次阅读代码都会十分痛苦;
1313+ 各种各样的计算都需要用到大量的临时变量来存储,使得变量命名较为容易发生混乱;
1414+ 如果只有一份文件,那么多人进行协同开发听起来更像是天方夜谭;
1515+ 在进行debug调试的时候,如此巨大的代码量会让人无从下手。
1616
17- 所以,一种对代码进行抽象的封装技术便应运而生了 --函数。
17+ 所以,一种对代码进行抽象的封装技术便不可或缺了 --函数。
1818
19- 首先,如果让我们打印一首辛弃疾的词,我们知道了前面的内容之后可以这样 :
19+ 首先,如果让我们打印一首辛弃疾的词,我们在学习完前面的内容之后可以这样 :
2020
2121~~~ python
2222print " 丑奴儿·书博山道中壁"
@@ -26,9 +26,9 @@ print "而今识尽愁滋味,欲说还休。"
2626print " 欲说还休,却道天凉好个秋。"
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
3434def print_XinQiji_SongPoems ():
@@ -42,8 +42,8 @@ print_XinQiji_SongPoems()
4242print_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``
7394question:那么问题来了,第一节中的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