|
2 | 2 |
|
3 | 3 | #函数(2)
|
4 | 4 |
|
5 |
| -在上一节中,已经明确了函数的基本结构和初步的调用方法。但是,上一节中写的函数,还有点缺憾,不知道读者是否觉察到了。我把结果是用`print`语句打印出来的。这是实际编程中广泛使用的吗?肯定不是。在程序中,函数是一段具有抽象作用的代码。一般情况下,通过它可以得到某个结果,这个结果有一个专有名字,叫做“返回值”。返回值会继续被用到程序中的某个地方。 |
6 |
| - |
7 | 5 | ##返回值
|
8 | 6 |
|
9 |
| -为了能够说明清楚,先编写一个函数。还记得斐波那契数列吗?忘了没关系,回头看看或者google。不过,在你要实施google或者顺延着向下阅读之前,最好先自己尝试一下,能不能写一个斐波那契数列的函数。 |
| 7 | +所谓返回值,就是函数向调用函数的地方返回的数据。 |
| 8 | + |
| 9 | +编写一个斐波那契数列函数,来说明这个问题。还记得斐波那契数列吗?忘了没关系,看看本教程前面的内容即可。 |
10 | 10 |
|
11 | 11 | 我这里提供一段参考代码(既然是参考,显然不是唯一正确答案):
|
12 | 12 |
|
|
23 | 23 | lst = fibs(10)
|
24 | 24 | print lst
|
25 | 25 |
|
26 |
| -把含有这些代码的文件保存为名为20202.py的文件。在这个文件中,首先定义了一个函数,名字叫做fibs,其参数是输入一个整数(但是,你并没有看到我在哪里做了对这个要输入的值的约束,就意味着,你输入非整数,甚至字符串,也使可以的,只是结果会不同,不妨试试吧),然后通过`lst = fibs(10)`调用这个函数。这里参数给的是10,就意味着要得到n=10的斐波那契数列。 |
| 26 | +把含有这些代码的文件保存为名为20202.py的文件。 |
| 27 | + |
| 28 | +在这个文件中,首先定义了一个函数,名字叫做`fibs`,其参数是输入一个整数(但是,你并没有看到我在哪里做了对这个要输入的值的约束,就意味着,你输入非整数,甚至字符串,也使可以的,只是结果会不同,不妨试试吧),然后通过`lst = fibs(10)`调用这个函数。这里参数给的是10,就意味着要得到`n=10`的斐波那契数列。 |
27 | 29 |
|
28 | 30 | 运行后打印数列:
|
29 | 31 |
|
|
32 | 34 |
|
33 | 35 | 当然,如果要换n的值,只需要在调用函数的时候,修改一下参数即可。这才体现出函数的优势呢。
|
34 | 36 |
|
35 |
| -观察fibs函数,最后有一个语句`return result`,意思是将变量result的值返回。返回给谁呢?这要看我们当前在什么位置调用该函数了。在上面的程序中,以`lst = fibs(10)`语句的方式,调用了函数,那么函数就将值返回到当前状态,并记录在内存中,然后把它赋值给变量lst。如果没有这个赋值语句,函数照样返回值,但是它飘忽在内存中,我们无法得到,并且最终还被当做垃圾被python回收了。 |
| 37 | +观察`fibs()`函数,最后有一个语句`return result`,意思是将变量result的值返回。返回给谁呢?这要看我们当前在什么位置调用该函数了。 |
| 38 | + |
| 39 | +在上面的程序中,以`lst = fibs(10)`语句的方式,调用了函数,那么函数就将值返回到当前状态,并记录在内存中,然后把它赋值给变量`lst`。 |
36 | 40 |
|
37 | 41 | 注意:上面的函数只返回了一个返回值(是一个列表),有时候需要返回多个,是以元组形式返回。
|
38 | 42 |
|
39 | 43 | >>> def my_fun():
|
40 |
| - ... return 1,2,3 |
| 44 | + ... return 1, 2, 3 |
41 | 45 | ...
|
42 | 46 | >>> a = my_fun()
|
43 | 47 | >>> a
|
44 | 48 | (1, 2, 3)
|
| 49 | + |
| 50 | +对这个函数,我们还可以用这样的方式来接收函数的返回值。 |
45 | 51 |
|
46 |
| -有的函数,没有return,一样执行完毕,就算也干了某些活儿吧。事实上,不是没有返回值,也有,只不过是None。比如这样一个函数: |
| 52 | + >>> x, y, z = my_fun() |
| 53 | + >>> x |
| 54 | + 1 |
| 55 | + >>> y |
| 56 | + 2 |
| 57 | + >>> z |
| 58 | + 3 |
47 | 59 |
|
48 |
| - >>> def my_fun(): |
49 |
| - ... print "I am doing somthin." |
| 60 | +多么神奇。 |
| 61 | + |
| 62 | +也不怎么神奇,这也来源于我们前面已经熟知的赋值语句。其效果相当于: |
| 63 | + |
| 64 | + >>> x, y, z = a |
| 65 | + >>> x, y, z |
| 66 | + (1, 2, 3) |
| 67 | + |
| 68 | +不是所有的函数都有`return`的,比如有的函数,就是执行某个语句或者什么也不做,不需要返回值。事实上,不是没有返回值,也有,只不过是None。比如这样一个函数: |
| 69 | + |
| 70 | + >>> def foo(): |
| 71 | + ... pass |
50 | 72 | ...
|
51 | 73 |
|
52 | 74 | 我在交互模式下构造一个很简单的函数,注意,我这是构造了一个简单函数,如果是复杂的,千万不要在交互模式下做。如果你非要做,是能尝到苦头的。
|
53 | 75 |
|
54 |
| -这个函数的作用就是打印出一段话。也就是执行这个函数,就能打印出那段话,但是没有return。 |
| 76 | +这个函数的作用就是pass——什么也不做,当然是没有return了。 |
55 | 77 |
|
56 |
| - >>> a = my_fun() |
57 |
| - I am doing somthin. |
| 78 | + >>> a = foo() |
58 | 79 |
|
59 | 80 | 我们再看看那个变量a,到底是什么
|
60 | 81 |
|
61 |
| - >>> print a |
| 82 | + >>> print a #Python 3: print(a) |
62 | 83 | None
|
63 | 84 |
|
64 |
| -这就是只干活儿,没有`return`的函数,事实上返回的是一个`None`。这种模样的函数,通常不用上述方式调用,而采用下面的方式,因为他们返回的是None,似乎这个返回值利用价值不高,于是就不用找一个变量来接受返回值了。 |
| 85 | +这就是没有`return`的函数,事实上返回的是一个`None`。而`None`,你有可以理解成没有返回任何东西。 |
65 | 86 |
|
66 |
| - >>> my_fun() |
67 |
| - I am doing somthin. |
| 87 | +这种模样的函数,通常不用上述方式调用,而采用下面的方式,因为他们返回的是None,似乎这个返回值利用价值不高,于是就不用找一个变量来接受返回值了。 |
| 88 | + |
| 89 | + >>> foo() |
68 | 90 |
|
69 |
| -特别注意那个return,它还有一个作用,请先观察下面的函数和执行结果,并试图找出其作用。 |
| 91 | +特别注意那个`return`,它还有一个作用,请先观察下面的函数和执行结果,并试图找出其作用。 |
70 | 92 |
|
71 | 93 | >>> def my_fun():
|
72 |
| - ... print "I am coding." |
| 94 | + ... print "I am coding." #Python 3的用户请修改为print() |
73 | 95 | ... return
|
74 | 96 | ... print "I finished."
|
75 | 97 | ...
|
76 | 98 | >>> my_fun()
|
77 | 99 | I am coding.
|
78 | 100 |
|
79 |
| -看出玄机了吗?在函数中,本来有两个print语句,但是中间插入了一个return,仅仅是一个return。当执行函数的时候,只执行了第一个print语句,第二个并没有执行。这是因为第一个之后,遇到了return,它告诉函数要返回,即中断函数体内的流程,离开这个函数。结果第二个print就没有被执行。所以,return在这里就有了一个作用,结束正在执行的函数,有点类似循环中的break的作用。 |
| 101 | +看出玄机了吗? |
| 102 | + |
| 103 | +在函数中,本来有两个`print`,但是中间插入了一个`return`,仅仅是一个`return`。当执行函数的时候,只执行了第一个`print`,第二个并没有执行。这是因为第一个之后,遇到了return,它告诉函数要返回,即中断函数体内的流程,离开这个函数。结果第二个`print`就没有被执行。所以,`return`在这里就有了一个作用,结束正在执行的函数,并离开函数体返回到调用位置,有点类似循环中的`break`的作用。 |
80 | 104 |
|
81 | 105 | ##函数中的文档
|
82 | 106 |
|
83 |
| -“程序在大多数情况下是给人看的,只是偶尔被机器执行。”所以,写程序必须要写注释。前面已经有过说明,如果用`#`开始,python就不执行那句(python看不到它,但是人能看到),它就作为注释存在。 |
| 107 | +“程序在大多数情况下是给人看的,只是偶尔被机器执行。” |
| 108 | + |
| 109 | +所以,写程序必须要写注释。前面已经有过说明,如果用`#`开始,Python就不执行那句(Python看不到它,但是人能看到),它就作为注释存在。 |
84 | 110 |
|
85 | 111 | 除了这样的一句之外,一般在每个函数名字的下面,还有比较多的说明,这个被称为“文档”,在文档中主要是说明这个函数的用途。
|
86 | 112 |
|
|
120 | 146 | my_fun()
|
121 | 147 | This is my function.
|
122 | 148 |
|
123 |
| -##参数和变量 |
| 149 | +##函数的属性 |
124 | 150 |
|
125 |
| -###参数 |
| 151 | +任何对象都具有属性,比如“孔乙己的茴香豆”,这里“孔乙己”是一个对象,“茴香豆”是一个属性,世界上“茴香豆”很多,但是这里所说的“茴香豆”是比较特殊的,它归属于“孔乙己”。如果用符号的方式来表示“孔乙己的茴香豆”,一般习惯用句点(英文的)代替中间的“的”子,也就是句点表示了属性的归属,表示为:`孔乙己.茴香豆`。 |
126 | 152 |
|
127 |
| -虽然在上一节,已经知道如何通过函数的参数传值,如何调用函数等。但是,这里还有必要进一步讨论参数问题。在别的程序员嘴里,你或许听说过“形参”、“实参”、“参数”等名词,到底指什么呢? |
| 153 | +前面已经说过,函数是对象。那么它也有属性。 |
128 | 154 |
|
129 |
| ->在定义函数的时候(def来定义函数,称为def语句),函数名后面的括号里如果有变量,它们通常被称为“形参”。调用函数的时候,给函数提供的值叫做“实参”,或者“参数”。 |
| 155 | + >>> def cang(): |
| 156 | + """This is a function of canglaoshi""" |
| 157 | + pass |
130 | 158 |
|
131 |
| -其实,根本不用区分这个,因为没有什么意义,只不过类似孔乙己先生知道茴香豆的茴字有多少种写法罢了。 |
| 159 | +对于这个函数,最熟悉的一个属性就应该是前面提到的函数文档,它可以用句点的方式表示为`cang.__doc__`。 |
132 | 160 |
|
133 |
| -**在本教程中,把那个所谓实参,就称之为值(或者数据、或者对象),形参就笼统称之为参数(似乎不很合理,但是接近数学概念)。**随着你敲代码的实践越多,或许会对各种参数概念有深入理解。 |
| 161 | + >>> cang.__doc__ |
| 162 | + 'This is a function of canglaoshi' |
134 | 163 |
|
135 |
| -###比较参数和变量 |
| 164 | +这就体现出这种方式表示属性的优势了,只要对象不同,不管属性的名字是否相同,用句点就可以说明该属性所对应的对象。 |
136 | 165 |
|
137 |
| -在不同的参数名称面前,糊涂也罢、明白也罢,对写程序的干扰不大。不过,对于变量和参数,这两个就不能算糊涂账了。不过它们的确容易让把人搞糊涂了。 |
| 166 | +还可以为对象增加属性。 |
138 | 167 |
|
139 |
| -在数学的函数中`y = 3x + 2`,那个x叫做参数,也可以叫做变量。但是,在编程语言的函数中,与此有异。 |
| 168 | + >>> cang.breast = 90 |
| 169 | + |
| 170 | +这样就为对象`cang`增加了一个属性`breast`,并且设置该属性的值是90。接下来就可以调用该属性。 |
| 171 | + |
| 172 | + >>> cang.breast |
| 173 | + 90 |
| 174 | + |
| 175 | +还记得我们用来查看对象属性和方法的函数`dir()`吗?现在又可以请它出来,一览众属性。 |
| 176 | + |
| 177 | + >>> dir(cang) |
| 178 | + ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'breast'] |
| 179 | + |
| 180 | +这里列出了所有`cang`这个对象的属性和方法,仔细观察,我们刚才用过的`cang.__doc__`和刚刚设置的`cang.breast`都历历在目。至于这里有很多属性的名字都是用双下划线开始和结束,这类属性可以称之为特殊属性(因为名字样式特殊吗?)。 |
| 181 | + |
| 182 | + >>> cang.__name__ |
| 183 | + 'cang' |
| 184 | + >>> cang.__module__ |
| 185 | + '__main__' |
| 186 | + |
| 187 | +所有这些属性,都可以用句点的方式调用。 |
| 188 | + |
| 189 | +##概念辨析 |
| 190 | + |
| 191 | +有几个概念,是编程的时候常用到的。从某个角度讲,世界就是用概念构成的。如果没有概念,很难准确描述清楚世界。 |
140 | 192 |
|
141 |
| -先参考一段来自[微软网站](http://msdn.microsoft.com/zh-cn/library/9kewt1b3.aspx)的比较高度抽象,而且意义涵盖深远的说明。我摘抄过来,看官读一读,是否理解,虽然是针对VB而言的,一样有启发。 |
| 193 | +###参数和变量 |
| 194 | + |
| 195 | +函数的参数,还是很有话题的。比如在别的程序员嘴里,你或许听说过“形参”、“实参”、“参数”等名词,到底指什么呢? |
| 196 | + |
| 197 | +>在定义函数的时候(def来定义函数,称为def语句),函数名后面的括号里如果有变量,它们通常被称为“形参”。调用函数的时候,给函数提供的值叫做“实参”,或者“参数”。 |
| 198 | +
|
| 199 | +其实,如果你区别不开,也不会耽误你写代码,这只不过类似孔乙己先生知道茴香豆的茴字有多少种写法罢了。但是,我居然碰到过某公司的面试官问这种问题。 |
| 200 | + |
| 201 | +我们就简化一下,笼统地把函数括号里面的变量叫做参数吧,当然你叫变量也无妨,只要大家知道值得是什么东西就好了。虽然这样会引起某些认真的人来喷口水,但也不用担心,反正本书已经声明是很“水”的了。 |
| 202 | + |
| 203 | +但如果有人较真,非要让你区分,为了显示你的水平,你可以引用[微软网站](http://msdn.microsoft.com/zh-cn/library/9kewt1b3.aspx)上的说明。我认为这段说明高度抽象,而且意义涵盖深远的说明。摘抄过来,请读一读,是否理解。 |
142 | 204 |
|
143 | 205 | >参数和变量之间的差异 (Visual Basic)
|
144 | 206 |
|
|
158 | 220 |
|
159 | 221 | >与形参定义不同,实参没有名称。每个实参就是一个表达式,它包含零或多个变量、常数和文本。求值的表达式的数据类型通常应与为相应形参定义的数据类型相匹配,并且在任何情况下,该表达式值都必须可转换为此形参类型。
|
160 | 222 |
|
161 |
| -看官如果硬着头皮看完这段引文,发现里面有几个关键词:参数、变量、形参、实参。本来想弄清楚参数和变量,结果又冒出另外两个东东,更混乱了。请稍安勿躁,本来这段引文就是有点多余,但是,之所以引用,就是让列位开阔一下眼界,在编程业界,类似的东西有很多名词。下次听到有人说这些,不用害怕啦,反正自己听过了。 |
| 223 | +如果硬着头皮看完这段引文,发现里面有几个关键词:参数、变量、形参、实参。本来想弄清楚参数和变量,结果又冒出另外两个词,更混乱了。请稍安勿躁,在编程业界,类似的东西有很多名词。下次听到有人说这些,不用害怕啦,反正自己听过了。 |
162 | 224 |
|
163 | 225 | 在Python中,没有这么复杂。
|
164 | 226 |
|
|
176 | 238 |
|
177 | 239 | 至此,是否清楚了一点点。当然,我所表述不正确之处或者理解错误之处,请不吝赐教,小可作揖感谢。
|
178 | 240 |
|
179 |
| -其实没有那么复杂。关键要理解函数名括号后面的东东(管它什么参呢)的作用是传递值。所以,那个参数的作用本质上就是一个“占位符”,当调用一个函数的时候,并不是赋值了一份参数的值来替换占位符,比如`add(x)`,并没有用3来替换原来的占位符,而是把占位符指向了变量,进而指向了对象。换个角度说,就是通过一连串的接力动作,把对象传给了函数。这样说来,你就可以在函数内部改变那个对象了。 |
| 241 | +其实没有那么复杂。关键要理解函数名括号后面的东东(管它什么参呢)的作用是“传对象引用”——这又是一种貌似高深的说法。 |
180 | 242 |
|
181 | 243 | >>> def foo(lst):
|
182 | 244 | ... lst.append(99)
|
|
195 | 257 |
|
196 | 258 | 结合前面学习过的列表能够被原地修改知识,加上刚才说的参数特点,你是不是能理解上面的操作呢?
|
197 | 259 |
|
198 |
| -##全局变量和局部变量 |
| 260 | +###全局变量和局部变量 |
| 261 | + |
| 262 | +虽然是讲参数,但是关于全局变量和局部变量的区别也要先弄清楚,因为关系到函数内外有别的大事。 |
199 | 263 |
|
200 |
| -下面是一段代码,注意这段代码中有一个函数funcx(),这个函数里面有一个变量x=9,在函数的前面也有一个变量x=2 |
| 264 | +下面是一段代码,注意这段代码中有一个函数`funcx()`,这个函数里面有一个`x=9`,在函数的前面也有一个`x=2`。 |
201 | 265 |
|
202 | 266 | x = 2
|
203 | 267 |
|
204 | 268 | def funcx():
|
205 | 269 | x = 9
|
206 |
| - print "this x is in the funcx:-->",x |
| 270 | + print "this x is in the funcx:-->", x #Python 3请自动修改为print(),下同,从略 |
207 | 271 |
|
208 | 272 | funcx()
|
209 | 273 | print "--------------------------"
|
210 |
| - print "this x is out of funcx:-->",x |
| 274 | + print "this x is out of funcx:-->", x |
211 | 275 |
|
212 |
| -那么,这段代码输出的结果是什么呢?看: |
| 276 | +这段代码输出的结果是什么呢?看: |
213 | 277 |
|
214 | 278 | this x is in the funcx:--> 9
|
215 | 279 | --------------------------
|
216 | 280 | this x is out of funcx:--> 2
|
217 | 281 |
|
218 |
| -从输出看出,运行funcx(),输出了funcx()里面的变量x=9;然后执行代码中的最后一行,print "this x is out of funcx:-->",x |
| 282 | +从输出中可以看出,运行`funcx()`,输出了`funcx()`里面的变量`x`引用的对象9;然后执行代码中的最后一行`print "this x is out of funcx:-->",x`。 |
219 | 283 |
|
220 |
| -特别要关注的是,前一个x输出的是函数内部的变量x;后一个x输出的是函数外面的变量x。两个变量彼此没有互相影响,虽然都是x。从这里看出,两个x各自在各自的领域内起到作用。 |
| 284 | +特别要关注的是,前一个`x`输出的是函数内部的变量`x`;后一个`x`输出的是函数外面的变量`x`。两个变量彼此没有互相影响,虽然都是`x`。两个`x`各自在各自的领域内起到作用。 |
221 | 285 |
|
222 | 286 | 把那个只在函数体内(某个范围内)起作用的变量称之为**局部变量**。
|
223 | 287 |
|
|
233 | 297 | print "--------------------------"
|
234 | 298 | print "this x is out of funcx:-->",x
|
235 | 299 |
|
236 |
| -以上两段代码的不同之处在于,后者在函数内多了一个`global x`,这句话的意思是在声明x是全局变量,也就是说这个x跟函数外面的那个x同一个,接下来通过x=9将x的引用对象变成了9。所以,就出现了下面的结果。 |
| 300 | +以上两段代码的不同之处在于,后者在函数内多了一个`global x`,这句话的意思是在声明`x`是全局变量,也就是说这个`x`跟函数外面的那个`x`同一个,接下来通过`x=9`将x的引用对象变成了9。所以,就出现了下面的结果。 |
237 | 301 |
|
238 | 302 | this x is in the funcx:--> 9
|
239 | 303 | --------------------------
|
240 | 304 | this x is out of funcx:--> 9
|
241 | 305 |
|
242 | 306 | 好似全局变量能力很强悍,能够统统率函数内外。但是,要注意,这个东西要慎重使用,因为往往容易带来变量的混乱。内外有别,在程序中一定要注意的。
|
243 | 307 |
|
244 |
| -##命名空间 |
| 308 | +###命名空间 |
245 | 309 |
|
246 | 310 | 这是一个比较不容易理解的概念,特别是对于初学者而言,似乎它很飘渺。我在维基百科中看到对它的定义,不仅定义比较好,连里面的例子都不错。所以,抄录下来,帮助读者理解这个名词。
|
247 | 311 |
|
|
0 commit comments