python函数
def函数定义
def (arg1, arg2,... argN):
return
与C这样的编译语言不同,Python函数在程序运行之前并不需要全部定义。更确切地讲,def在运行时才进行评估,而在def之中的代码在函数调用后才会评估。
global语句
它并不是一个类型或大小的声明,它是一个命名空间的声明。它告诉Python函数打算生成一个或多个全局变量名。也就是说,存在于整个模块内部作用域(命名空间)的变量名。
X = 88 # Global X
def func():
global X
X = 99 # Global X: outside deffunc()
print(X) # Prints 99
y, z = 1, 2 # Global variables in module
def all_global():
global x # Declare globals assigned
x = y + z # No need to declare y, z: LEGB rule
原则
- 最小化全局变量
- 最小化文件间的修改
这种隐含的跨文件依赖性,在最好的情况下会导致代码不灵活,最坏的情况下会引发bug。
# first.py
X = 99 # This code doesn't know about second.py
# second.py
import first
print(first.X) # Okay: references a name in another file
first.X = 88 # But changing it can be too subtle and implicit
升级后
# first.py
X = 99
def setX(new):
global X
X = new
# second.py
import first
first.setX(88)
文件模块使用全局变量
# thismod.py
var = 99 # Global variable == module attribute
def local():
var = 0 # Change local var
def glob1():
global var # Declare global (normal)
var += 1 # Change global var
def glob2():
var = 0 # Change local var
import thismod # Import myself
thismod.var += 1 # Change global var
def glob3():
var = 0 # Change local var
import sys # Import system table
glob = sys.modules['thismod'] # Get module object (or use __name__)
glob.var += 1 # Change global var
def test():
print(var) #99
local(); glob1(); glob2(); glob3()
print(var) #102
嵌套作用域举例
def f1():
X = 88
def f2():
print(X) # Remembers X in enclosing def scope
return f2 # Return f2 but don't call it
action = f1() # Make, return function
action() # Call it now: prints 88
在这个代码中,我们命名为f2的函数的调用动作的运行是在f1运行后发生的。f2记住了在f1中嵌套作用域中的x,尽管f1已经不处于激活状态。有点费解 这都可以。
通常来说,类是一个更好的像这样进行“记忆”的选择,因为它们让状态变得很明确。不使用类的话,全局变量、像这样的嵌套作用域引用以及默认的参数就是Python的函数能够保留状态信息的主要方法了。为
nonlocal语句
nonlocal和global一样,声明了将要在一个嵌套的作用域中修改的名称。和global的不同之处在于,nonlocal应用于一个嵌套的函数的作用域中的一个名称,而不是所有def之外的全局模块作用域;
>>>def tester(start):
... state = start # Each call gets its own state
... def nested(label):
... nonlocal state # state必须提前声明 不然会报错
... print(label, state)
... state += 1 # Allowed to change it if nonlocal
... return nested
...
>>>F = tester(0)
>>>F('spam') # Increments state on each call
spam 0
>>>F('ham')
ham 1
>>>F('eggs')
eggs 2
函数参数
- 不可变参数“通过值”进行传递。像整数和字符串这样的对象是通过对象引用而不是拷贝进行传递的,但是因为你无论怎样都不可能在原处改变不可变对象,实际的效果就很像创建了一份拷贝。
- 可变对象是通过“指针”进行传递的。例如,列表和字典这样的对象也是通过对象引用进行传递的,这一点与C语言使用指针传递数组很相似:可变对象能够在函数内部进行原处的改变,这一点和C数组很像。
不可变对象
>>> x = 1
>>> id(x)
19852392
>>> x +=1
>>> id(x)
19852368
不可变对象的函数传参
>>>def f(a): # a is assigned to (references) passed object
... a = 99 # Changes local variable a only
...
>>>b = 88
>>>f(b) # a and b both reference same 88 initially
>>>print(b) # b is not changed
88
可变对象的函数传参
>>>def changer(a, b): # Arguments assigned references to objects
... a = 2 # Changes local name's value only
... b[0] = 'spam' # Changes shared object in-place
...
>>>X = 1
>>>L = [1, 2] # Caller
>>>changer(X, L) # Pass immutable and mutable objects
>>>X, L # X is unchanged, L is different!
(1, ['spam', 2])
避免可变参数的修改
方式一
L = [1, 2]
changer(X, L[:]) # Pass a copy, so our 'L' does not change
方式二
def changer(a, b):
b = b[:] # Copy input list so we don't impact caller
a = 2
b[0] = 'spam' # Changes our list copy only
方式三 转换为元组
L = [1, 2]
changer(X, tuple(L)) # Pass a tuple, so changes
特定的参数匹配模型
位置:从左至右进行匹配
一般情况下,也是我们迄今为止最常使用的那种方法,是通过位置进行匹配把参数值传递给函数头部的参数名称,匹配顺序为从左到右。关键字参数:通过参数名进行匹配
调用者可以定义哪一个函数接受这个值,通过在调用时使用参数的变量名,使用name=value这种语法。默认参数:为没有传入值的参数定义参数值
如果调用时传入的值过于少的话,函数能够为参数定义接受的默认值,再一次使用语法name=value。可变参数:收集任意多基于位置或关键字的参数
函数能够使用特定的参数,它们是以字符*开头,收集任意多的额外参数(这个特性常常叫做可变参数,类似C语言中的可变参数特性,也能够支持可变长度参数的列表)。
可变参数解包:传递任意多的基于位置或关键字的参数
调用者能够再使用*语法去将参数集合打散,分成参数。这个“*”与在函数头部的“*”恰恰相反:在函数头部它意味着收集任意多的参数,而在调用者中意味着传递任意多的参数。Keyword-only参数:参数必须按照名称传递
在Python 3.0中(不包括Python2.6中),函数也可以指定参数,参数必须用带有关键参数的名字(而不是位置)来传递。这样的参数通常用来定义实际参数以外的配置选项。
关键字参数
>>>def f(a, b, c): print(a, b, c)
#普通调用
>>>f(1, 2, 3)
#关键字参数
>>>f(c=3, b=2, a=1)
#默认参数
>>>def f(a, b=2, c=3): print(a, b, c)
>>>f(1)
#收集参数
>>>def f(*args): print(args)
>>>f(1, 2, 3, 4)(1, 2, 3, 4)
>>>def f(a, *pargs, **kargs): print(a, pargs, kargs)
>>>f(1, 2, 3, x=1, y=2)
1 (2, 3) {'y': 2, 'x': 1}
#解包参数
#元组
>>>def func(a, b, c, d): print(a, b, c, d)
...
>>>args = (1, 2)
>>>args += (3, 4)
>>>func(*args)
1 2 3 4
#字典
>>>args = {'a': 1, 'b': 2, 'c': 3}
>>>args['d'] = 4
>>>func(**args)
varargs调用语法 暂时不动有卵用
def tracer(func, *pargs, **kargs): # Accept arbitrary arguments
print('calling:', func.__name__)
return func(*pargs, **kargs) # Pass along arbitrary arguments
def func(a, b, c, d):
return a + b + c + d
print(tracer(func, 1, 2, c=3, d=4))
Keyword-Only参数
keyword-only参数——即必须只按照关键字传递并且不会由一个位置参数来填充的参数。
>>>def kwonly(a, *, b, c='spam'):
... print(a, b, c)
...
>>>kwonly(1, b='eggs')
1 eggs spam
>>>kwonly(1, c='eggs')
TypeError: kwonly() needs keyword-only argument b
>>>kwonly(1, 2)
TypeError: kwonly() takes exactly 1 positional argument (2 given)
keyword-only参数必须在一个单个星号后面指定,而不是两个星号——命名的参数不能出现在**args任意关键字形式的后面,并且一个**不能独自出现在参数列表中。这两种做法都将产生语法错误:
#这里不用怀疑 语法分析 那块估计会有问题
>>>def kwonly(a, **pargs, b, c):
SyntaxError: invalid syntax
>>>def kwonly(a, **, b, c):
SyntaxError: invalid syntax