一、函数的返回值
In [4]: def add(x, y): ...: return x+y ...: In [5]: add(1, 2) Out[5]: 3 In [8]: def add(x, y): ...: return x+y ...: print(x+y) ...: In [9]: add(1, 2) Out[9]: 3 In [16]: def add(x, y): ...: return "abc", x+y ...: return x+y ...: In [17]: add(1, 2) Out[17]: (‘abc‘, 3) In [18]: def add(x, y): ...: return ...: ...: In [19]: add(1, 2) In [20]: a = add(1, 2) In [21]: a In [22]: type(a) Out[22]: NoneType In [25]: def add(x, y): ...: return None ...: In [26]: add(1, 2) In [27]: type(add(1, 2)) Out[27]: NoneType
关键字:return
return只能出现在函数中,可以返回任何对象,可以作为元祖变相的返回多个值
所有函数都有返回值,如果没定义return则默认返回值None
return语句除了返回值之外还会结束函数
1个函数可以有多个return语句,但只会执行一个
二、作用域
1、函数嵌套
函数可以嵌套定义
In [28]: def outter(): ...: def inner(): ...: print(‘inner‘) ...: print(‘outter‘) ...: inner() ...: In [29]: outter() outter inner
2、作用域
作用域是一个变量的可见范围
函数内部是一个局部作用域,外面叫全局作用域
不同作用域变量不可见,但是上级作用域的变量对下级只读可见
In [32]: x = 1 In [33]: def inc(): ...: x += 1 # x此时是局部变量,不能直接使用全局作用域的变量 ...: In [34]: inc() --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) <ipython-input-34-ae671e6b904f> in <module>() ----> 1 inc() <ipython-input-33-661b9217054c> in inc() 1 def inc(): ----> 2 x += 1 3 UnboundLocalError: local variable ‘x‘ referenced before assignment In [40]: x = 1 In [41]: def fn(): ...: print(x) ...: In [42]: fn() # 为什么这里能打印出来,不抛出错误呢 1
变量的作用域为定义此变量的作用域:
In [43]: def fn(): ...: name = "xxj" ...: print(name) ...: In [44]: fn() xxj In [45]: name --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-45-18697449d7c4> in <module>() ----> 1 name NameError: name ‘name‘ is not defined
上级作用域对下级作用域是只读可见的
In [46]: def fn(): ...: xx = 1 ...: print(xx) ...: def inner(): ...: print(xx) ...: inner() ...: In [47]: fn() 1 1 In [48]: def fn(): ...: xx = 1 ...: print(xx) ...: def inner(): ...: xx = 2 ...: print(xx) ...: inner() ...: print(xx) ...: In [49]: fn() 1 2 1
函数中可以显式定义提升变量作用域,global的提升只在本作用域及上级作用域有效:
In [53]: xx = 1 In [54]: def fn(): ...: global xx # global关键字能显式的提升1个变量的作用域 ...: xx += 1 ...: In [55]: fn() In [56]: xx Out[56]: 2 In [57]: fn() In [58]: xx Out[58]: 3 In [68]: xxj --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-68-bc382da45e82> in <module>() ----> 1 xxj NameError: name ‘xxj‘ is not defined In [69]: def fn(): ...: global xxj # 如果此变量没有定义,则此提升变量作用域没有意义 ...: ...: In [70]: fn() In [71]: xxj --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-71-bc382da45e82> in <module>() ----> 1 xxj NameError: name ‘xxj‘ is not defined
除非你清楚的知道global会带来什么,并且明确的知道没有global不行的话,否则不要用global;就是不建议使用global关键字
3、命名空间与LEGB
1)命名空间
理解Python的LEGB原则是理解Python命名空间的关键,而理解Python的命名空间又是理解Python中许多语法规定的关键。所以,Python的LEGB原则就成为Python中一个非常核心的内容
白话一点讲:命名空间是对变量名的分组划分。
不同组的相同名称的变量视为两个独立的变量,因此隶属于不同分组(即命名空间)的变量名可以重复。
命名空间可以存在多个,使用命名空间,表示在该命名空间中查找当前名称。
命名空间表示变量的可见范围,一个变量名可以定义在多个不同的命名空间,相互之间并不冲突,但同一个命名空间中不能有两个相同的变量名。
比如:两个叫“张三”的学生可以同时存在于班级A和班级B中,如果两个张三都是一个班级,那么带来的麻烦复杂很多了,在Python中你不能这么干。
在Python中用字典来表示一个命名空间,命名空间中保存了变量(名字)和对象的映射关系,在Python中命名空间出现在哪些地方呢?有函数范围内的命名空间(local),有模块范围内的命名空间(global),有python内建的命名空间(built-in),还有类对象的所有属性组成的命名空间
Python一切皆对象,所以在Python中变量名是字符串对象
例如:
1 |
In [25]: a=10
|
表示建立字符串对象a
与Number对象10
之间的对应关系。由于这是一种映射关系,所以,可以使用键-值的形式来表示,即{name : object}。
前面已经说过,命名空间是对变量名的分组划分,所以,Python的命名空间就是对许多键-值对的分组划分,即,键值对的集合,因此:
Python的命名空间是一个字典,字典内保存了变量名称与对象之间的映射关系
2)命名空间的生命周期
所有的命名空间都是有生命周期的,对于python内建的命名空间,python解析器启动时创建,一直保留直至直python解析器退出时才消亡。而对于函数的local命名空间是在函数每次被调用的时候创建,调用完成函数返回时消亡,而对于模块的global命名空间是在该模块被import的时候创建,解析器退出时消亡。
3)作用域
一个作用域是指一段程序的正文区域,可以是一个函数或一段代码。
一个变量的作用域是指该变量的有效范围。Python的作用域是静态作用域,因为它是由代码中得位置决定的,而命名空间就是作用域的动态表现。
函数定义了本地作用域,而模块定义了全局作用域:
每个模块都是一个全局作用域,因此,全局作用域的范围仅限于单个程序文件
每次对函数的调用都会创建一个新的本地作用域,赋值的变量除非声明为全局变量,否则均为本地变量
所有的变量名都可以归纳为本地,全局或内置的(由__builtin__模块提供)
4)LEGB原则
LEGB含义解释:
L-Local(function);函数内的名字空间
E-Enclosing function locals;外部嵌套函数的名字空间(例如closure)
G-Global(module);函数定义所在模块(文件)的名字空间
B-Builtin(Python);Python内置模块的名字空间,builtin作用域,对应builtin命名空间,python内部定义的最顶层的作用域,在这个作用域里面定义了各种内建函数:open、range、xrange、list等等
前面讲到,Python的命名空间是一个字典,字典内保存了变量名与对象之间的映射关系,
因此,查找变量名就是在命名空间字典中查找键-值对。
Python有多个命名空间,因此,需要有规则来规定,按照怎样的顺序来查找命名空间,LEGB就是用来规定命名空间查找顺序的规则。
LEGB规定了查找一个名称的顺序为:local-->enclosing function locals-->global-->builtin
三、闭包
当一个函数结束了,函数的内部部分变量引用还存在,这就叫闭包
python的闭包可以使用可变容器实现,这也是python2唯一的方式
In [91]: def counter(): ...: c = [0] ...: def inc(): ...: c[0] += 1 ...: return c ...: return inc ...: In [92]: type(counter) Out[92]: function In [93]: type(counter()) Out[93]: function In [94]: type(counter()()) Out[94]: list In [95]: f = counter() In [96]: f() Out[96]: [1] In [97]: f() Out[97]: [2] In [98]: f() Out[98]: [3]
nonlocal关键字:
nonlocal用于标记一个变量由它的上级作用域定义,通过nonlocal标记的变量可读可写
如果nonlocal标记的变量不存在则会抛出语法错误
In [102]: def counter(): ...: x = 0 ...: def inc(): ...: nonlocal x ...: x += 1 ...: return x ...: return inc ...: In [103]: f = counter() In [104]: f() Out[104]: 1 In [105]: f() Out[105]: 2 In [106]: f() Out[106]: 3