函数
一、函数的定义
def是可执行的代码。def创建了一个对象并将其赋值给某一变量名。def语句是实时执行的,即:def在运行时才进行评估,而在def之中的代码在函数调用后才会评估。函数本身就是一个变量。
def (arg1,arg2,...): ... return
匿名函数lambda:
它就是一个表达式,而不是一个语句,可以放在def不能用的地方。lambda的主体是一个单个的表达式,而不是一个代码块。
例如:
def func(): x = 4 action = (lambda n: x ** n) return action a = func() print(a(2)) # 输出 16, 4**2
函数中重要的几点:
- lambda创建一个对象但将其作为结果返回。
- return将一个结果对象发送给调用者。
- yield向调用者发回一个结果对象,但是记住它离开的地方。
- global声明一个模块级的变量并被赋值。
- nonlocal声明了将要赋值的一个封闭的函数变量。
- 函数是通过赋值(对象引用)传递的。
- 参数、返回值以及变量并不是声明。
函数的设计概念
- 耦合性:对于输入使用参数并且对于输出使用return语句。
- 耦合性:只有在真正必要的情况下使用全局变量。
- 耦合性:不要改变可变类型的参数,除非调用者希望这样做。
- 聚合性:每一个函数都应该有一个单一的、统一的目标。
- 大小:每一个函数应该相对较小。如果一个函数很长了,层级很多了,那就应该拆分了。
- 耦合:避免直接改变在另一个模块文件中的变量。
二、函数的参数问题
1.参数分类
(1). 位置参数
按位置一一对应传参;
例如:
func('wjz',22,'IT')
(2). 关键字参数
key/value形式传参;
例如:
func(name='wjz',age=22,job='IT')
(3). 默认参数
默认参数,形参位置不指定传参时,该参数的默认值;
例如:
def func(name,age,job='IT')
(4). 无命名不定长参数
*args 位置参数的组合;可以看做一个元组;调用函数时有两种方式:多个位置参数形式;*[list] 列表形式。
例如:加法器
def func(*args):
count=0
for i in args:
count+=i
print(count)
func(1,2,3,4,5) # 调用,里面数字可以任意多。
(5). 有命名不定长参数
**kwargs 关键字参数组合;可以看做一个字典;调用函数时有两种方式:多个关键字参数形式;**{key:value} 字典形式。
例如:
def func(**kwargs):
for i in kwargs:
print('%s: %s'%(i,kwargs[i]))
func(name='wjz',age=22,job='IT') # 调用,里面的key/value可以任意多。
2. 位置参数和关键字参数分隔符
用*间隔; *号之前的参数可以是位置参数或关键字参数;*之后的参数必须是关键字参数;当有*args和**kwargs时,不能使用*号。
例如:
def func(name,age,*,job):
print("name:%s" %name)
print("age:%d" %age)
print("job:%s" %job)
func('wjz',age=22,job='IT') # job='IT' 必须是关键字参数传参
3. 参数顺序
从左到右的顺序:
位置参数 ---> 关键字参数 ---> *args ---> 默认参数(我推荐放这里) ---> **kwargs
这里有个问题,默认参数和*args顺序的问题,我一直没分清真正的大牛是怎么使用的,下面是我实验的结果。我是推荐放在 *args后面
- 如果默认参数在前面,没有指定默认参数的实参时,会把*args的第一个参数给默认参数;并且如果有*args参数时,无法使用关键字参数指定默认参数。
- 如果默认参数在后面,调用函数指定默认参数时必须是关键字形式,并且必须在*args的后面。最好是避免默认参数和*args一起使用。官方网站给出的例子是默认参数放在了*args后面。如下:
另外,关键字参数不能写在位置参数前面。如果位置参数和关键字参数混合的,则关键则参数必须放在位置参数后面,且关键字参数不能有前面位置参数已经占位的参数。如:
def func(a,b,c,d): print(a,b,c,d) func(1,2,d=5,b=1) # 这个会报错:func() got multiple values for argument 'b'。因为位置参数已经把2赋值给了b; func(1,2,d=5,c=1) # 这个正常,输出 1 2 1 5
三、函数的返回return
return的作用:让函数返回指定的值,便于我们对函数的正确执行与否做出判断,并且便于我们取函数执行的值。
- 函数在执行过程中,遇到return,则终止执行,并返回return指定的值;
- 当函数没有指定return时,默认返回 None;如果指定了return,则返回指定的对象。如果指定的是多个对象,解释器会把多个对象封装成一个元组;
四、函数的作用域
- 内嵌的模块是全局作用域;
- 全局作用域的作用范围仅限于单个文件;
- 每次对函数的调用都创建了一个新的本地作用域;
- 赋值的变量名除非声明为全局变量或非本地变量,否则均为本地变量;
- 所有其他的变量名都可以归纳为本地、全局或者内置的。
变量名解析遵循LEGB原则:
变量名引用分为三个作用域进行查找:首先是本地,之后是函数内,之后全局,最后是内置。 在默认情况下,变量名赋值会创建或者改变本地变量。 全局声明和非本地声明将赋值的变量名映射到模块文件内部的作用域。 LEGB原则:在3.0中,使用nonlocal解决作用域问题,应该叫LNGB原则。L即为local(函数中直接赋值的即默认为local);E即为Enclosing function locals(嵌套变量);G即为Global;B即为Built-in(内置变量); 例如: x=10 # 全局变量global def f(): t=5 # 嵌套变量 enclosing def inner(): count=7 # 本地变量 local return 1
global语句
global语句并不是一个类型或大小的声明,它是一个命名空间的声明。它告诉python函数生成一个或多个全局变量名。
全局变量名:
- 全局变量是位于模块文件内部的顶层的变量名;
- 全局变量如果是在函数内被赋值的话,必须经过声明;
- 全局变量名在函数的内部不经过声明也可以被引用;
nonlocal语句
nonlocal语句和global是近亲;它们两个一样,声明了将要在一个嵌套的作用域中修改的名称。和global不同的是,nonlocal应用于一个嵌套的函数的作用域中的一个名称,而不是所有def之外的全局模块作用域;而且在声明nonlocal名称的时候,它必须已经存在于该嵌套函数的作用域中。即nonlocal允许对嵌套的函数作用域中的名称赋值,并且把这样的名称的作用域查找限制在嵌套的def。
def func(): nonlocal name1,name2,...
例如:
def tester(start): state = start def nested(label): print(label,state) state +=1 return nested F = tester(0) F('spam') # 这时会报错:UnboundLocalError: local variable 'state' referenced before assignment
使用nonlocal进行修改:
def tester(start): state = start def nested(label): nonlocal state print(label,state) state +=1 return nested F = tester(0) F('spam') # 输出 spam 0 F('jack') # 输出 jack 1
有几点需要注意:
-
当执行一条nonlocal语句时,nonlocal名称必须已经在一个嵌套的def作用域中赋值过。否则会报错:no binding for nonlocal 'state' found.即变量state必须在nonlocal声明之前赋值。赋值语句可以是nonlocal上一层的def。
-
nonlocal限制作用域查找仅为嵌套的def,nonlocal不会在嵌套的模块的全局作用域或所有def之外的内置作用域中查找。