首页 > 代码库 > Python-功能函数的使用(三)
Python-功能函数的使用(三)
使用可选的可变参数定义函数
当使用有一个问题可选参数与可变默认类型,这可能会导致意外行为。
说明
这个问题是因为函数的默认参数初始化一旦被该功能时,在点定义,并不能(像许多其他语言)当函数被称为的默认值存储在函数对象的内部__defaults__
构件变量。
def f(a, b=42, c=[]): pass print(f.__defaults__) # Out: (42, [])
对于不可改变的类型因为没有办法变异的变量,这是没有问题的; 它只能被重新分配,使原始值不变。因此,后续保证具有相同的默认值。然而,对于一个可变类型,原始值可以发生变异,通过向它的各种部件的功能调用。因此,对函数的连续调用不能保证具有初始默认值。
def append(elem, to=[]): to.append(elem) # This call to append() mutates the default variable "to" return to append(1) # Out: [1] append(2) # Appends it to the internally stored list # Out: [1, 2] append(3, []) # Using a new created list gives the expected result # Out: [3] # Calling it again without argument will append to the internally stored list again append(4) # Out: [1, 2, 4]
注意:当一个可变类型被指定为默认属性像PyCharm有些IDE将发出警告。
解
如果你想确保缺省参数总是你在函数定义指定的,那么解决的办法是始终用一个不变的类型作为默认参数。
当可变类型,需要作为默认的一个常见的成语来实现这一点,是使用None
(不变)作为默认参数,然后实际的默认值赋给变量参数,如果它等于None
。
def append(elem, to=None): if to is None: to = [] to.append(elem) return to
参数传递和可变性
首先,一些术语:
- 参数(实参):实际变量被传递给函数。
- 参数(形式参数):即在一个函数中使用的接收变量。
在Python中,参数通过传递分配(相对于其他语言,其中的参数可以通过值/引用/指针传递)。
- 对参数进行突变会使参数发生变化(如果参数的类型是可变的)。
- 重新分配参数不会重新分配参数。
在Python中,我们不会给变量赋值。
- 相反,我们认为变量名称(标识,标签,标签),这些约束(分配,附后)为对象。
def foo(x): # The argument (y) is assigned to the parameter (x). x[0] = 9 # This mutates the list labelled by both x and y. x = [1, 2, 3] # x is now labelling a different list (y is unaffected). x[2] = 8 # This mutates x‘s list, not y‘s list. y = [4, 5, 6] # y is the argument, x is the parameter. foo(y) # Pretend that we wrote "x = y", then go to line 1. y # Out: [9, 5, 6]
- 一成不变的:整数,字符串,元组,依此类推。所有操作都复印。
- 可变:列表,字典,集合等。操作可能会或可能不会改变。
x = [3, 1, 9] y = x x.append(5) # Mutates the list labelled by both x and y. x.sort() # Mutates the list labelled by both x and y (in-place sorting). x = x + [4] # Does not mutate the list (makes a copy for x only, not y). z = x x += [6] # Mutates the list labelled by both x and z (uses the extend function). x = sorted(x) # Does not mutate the list (makes a copy for x only). x # Out: [1, 3, 4, 5, 6, 9] y # Out: [1, 3, 5, 9] z # Out: [1, 3, 5, 9, 4, 6]
关闭
python中的闭包是由函数调用创建的。在这里,呼吁makeInc
创建一个绑定x
是在函数内部引用inc
。每次调用makeInc
创建此函数的新实例,但每个实例有一个链接到不同的绑定x
。
def makeInc(x): def inc(y): # x is "attached" in the definition of inc return y + x return inc incOne = makeInc(1) incFive = makeInc(5) incOne (5) # returns 6 incFive(5) # returns 10
请注意,在常规闭包中,封闭函数完全从其封闭环境继承所有变量,在这个结构中,封闭函数只具有对继承变量的读访问权,但不能对它们进行赋值。
def makeInc(x): def inc(y): # incrementing x is not allowed x += y return x return inc incOne = makeInc(1) incOne(5) # UnboundLocalError: local variable ‘x‘ referenced before assignment
Python 3里提供的nonlocal
声明(非局部变量与嵌套函数实现了全封闭)。
def makeInc(x): def inc(y): nonlocal x # now assigning a value to x is allowed x += y return x return inc incOne = makeInc(1) incOne(5) # returns 6
从函数返回值
函数可以return
一个值,你可以直接使用:
def give_me_five(): return 5 print(give_me_five()) # Print the returned value # Out: 5
或保存该值以供以后使用:
num = give_me_five() print(num) # Print the saved returned value # Out: 5
或使用任何操作的值:
print(give_me_five() + 10) # Out: 15
如果return
在功能遇到功能将被立即退出和随后的操作不会进行评估:
def give_me_another_five(): return 5 print(‘This statement will not be printed. Ever.‘) print(give_me_another_five()) # Out: 5
你也可以return
多个值(以一元组的形式):
def give_me_two_fives(): return 5, 5 # Returns two 5 first, second = give_me_two_fives() print(first) # Out: 5 print(second) # Out: 5
用函数没有 return
语句隐返回None
。类似地,用函数return
声明,但没有返回值或变量的回报None
。
使用参数定义函数
参数在函数名后面的括号中定义:
def divide(dividend, divisor): # The names of the function and its arguments # The arguments are available by name in the body of the function print(dividend / divisor)
函数名和其的参数列表被称为签名的功能。每个命名参数实际上是函数的局部变量。
调用函数时,通过按顺序列出参数来提供参数的值。
divide(10, 2) # output: 5
或者使用函数定义中的名称以任意顺序指定它们:
divide(divisor=2, dividend=10) # output: 5
强制使用命名参数
在函数签名中的第一个星号后指定的所有参数都是仅限关键字的。
def f(*a, b): pass f(1, 2, 3) # TypeError: f() missing 1 required keyword-only argument: ‘b‘
在Python 3中,可以在函数签名中放置一个星号,以确保剩余的参数只能使用关键字参数传递。
def f(a, b, *, c): pass f(1, 2, 3) # TypeError: f() takes 2 positional arguments but 3 were given f(1, 2, c=3) # No error
递归限制
可能的递归的深度有一个限制,这取决于Python的实现。当达到限制时,会引发RuntimeError异常:
def cursing(depth): try: cursing(depth + 1) # actually, re-cursing except RuntimeError as RE: print(‘I recursed {} times!‘.format(depth)) cursing(0) # Out: I recursed 1083 times!
它可以通过使用改变递归深度限制sys.setrecursionlimit(limit)
,并检查由此限制sys.getrecursionlimit()
。
sys.setrecursionlimit(2000) cursing(0) # Out: I recursed 1997 times!
在Python 3.5中,例外的是一个RecursionError
,它是衍生自RuntimeError
。
递归函数
递归函数是在其定义中调用自身的函数。例如,数学函数,阶乘,由定义factorial(n) = n*(n-1)*(n-2)*...*3*2*1
。可编程为
def factorial(n): #n here should be an integer if n == 0: return 1 else: return n*factorial(n-1)
这里的输出是:
factorial(0) #out 1 factorial(1) #out 1 factorial(2) #out 2 factorial(3) #out 6
如预期。注意,这个功能是递归的,因为在第二return factorial(n-1)
,函数调用自身在其定义中。
有些递归函数可用来实现lambda,使用lambda阶乘函数将是这样的:
factorial = lambda n: 1 if n == 0 else n*factorial(n-1)
该功能的输出与上述相同。
使用多个参数定义函数
可以给出一个函数作为一个想要的参数,唯一固定的规则是每个参数名称必须是唯一的,可选参数必须在非可选参数之后:
def func(value1, value2, optionalvalue=http://www.mamicode.com/10): return ‘{0} {1} {2}‘.format(value1, value2, optionalvalue1)
调用该函数时,您可以给每个关键字不带名称,但顺序很重要:
print(func(1, ‘a‘, 100)) # Out: 1 a 100 print(func(‘abc‘, 14)) # abc 14 10
或者结合给出参数与名称和没有。然后名字的人必须遵循那些没有,但名称的顺序无关紧要:
print(func(‘This‘, optionalvalue=http://www.mamicode.com/‘StackOverflow Documentation‘, value2=‘is‘)) # Out: This is StackOverflow Documentation
迭代和字典解包
函数允许您指定这些类型的参数:位置,命名,变量位置,关键字args(kwargs)。这里是每个类型的清晰和简洁的使用。
def unpacking(a, b, c=45, d=60, *args, **kwargs): print(a, b, c, d, args, kwargs) >>> unpacking(1, 2) 1 2 45 60 () {} >>> unpacking(1, 2, 3, 4) 1 2 3 4 () {} >>> unpacking(1, 2, c=3, d=4) 1 2 3 4 () {} >>> unpacking(1, 2, d=4, c=3) 1 2 3 4 () {} >>> pair = (3,) >>> unpacking(1, 2, *pair, d=4) 1 2 3 4 () {} >>> unpacking(1, 2, d=4, *pair) 1 2 3 4 () {} >>> unpacking(1, 2, *pair, c=3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘c‘ >>> unpacking(1, 2, c=3, *pair) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘c‘ >>> args_list = [3] >>> unpacking(1, 2, *args_list, d=4) 1 2 3 4 () {} >>> unpacking(1, 2, d=4, *args_list) 1 2 3 4 () {} >>> unpacking(1, 2, c=3, *args_list) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘c‘ >>> unpacking(1, 2, *args_list, c=3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘c‘ >>> pair = (3, 4) >>> unpacking(1, 2, *pair) 1 2 3 4 () {} >>> unpacking(1, 2, 3, 4, *pair) 1 2 3 4 (3, 4) {} >>> unpacking(1, 2, d=4, *pair) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘d‘ >>> unpacking(1, 2, *pair, d=4) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘d‘ >>> args_list = [3, 4] >>> unpacking(1, 2, *args_list) 1 2 3 4 () {} >>> unpacking(1, 2, 3, 4, *args_list) 1 2 3 4 (3, 4) {} >>> unpacking(1, 2, d=4, *args_list) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘d‘ >>> unpacking(1, 2, *args_list, d=4) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘d‘ >>> arg_dict = {‘c‘:3, ‘d‘:4} >>> unpacking(1, 2, **arg_dict) 1 2 3 4 () {} >>> arg_dict = {‘d‘:4, ‘c‘:3} >>> unpacking(1, 2, **arg_dict) 1 2 3 4 () {} >>> arg_dict = {‘c‘:3, ‘d‘:4, ‘not_a_parameter‘: 75} >>> unpacking(1, 2, **arg_dict) 1 2 3 4 () {‘not_a_parameter‘: 75} >>> unpacking(1, 2, *pair, **arg_dict) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘d‘ >>> unpacking(1, 2, 3, 4, **arg_dict) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘d‘ # Positional arguments take priority over any other form of argument passing >>> unpacking(1, 2, **arg_dict, c=3) 1 2 3 4 () {‘not_a_parameter‘: 75} >>> unpacking(1, 2, 3, **arg_dict, c=3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unpacking() got multiple values for argument ‘c‘
嵌套函数
python中的函数是第一类对象。它们可以在任何范围内定义。
def fibonacci(n): def step(a,b): return b, a+b a, b = 0, 1 for i in range(n): a, b = step(a, b) return a
捕获它们的封闭范围的函数可以像任何其他类型的对象一样传递。
def make_adder(n): def adder(x): return n + x return adder add5 = make_adder(5) add6 = make_adder(6) add5(10) #Out: 15 add6(10) #Out: 16 def repeatedly_apply(func, n, x): for i in range(n): x = func(x) return x repeatedly_apply(add5, 5, 1) #Out: 26
递归Lambda使用分配的变量
创建递归lambda函数的一种方法包括将函数分配给变量,然后在函数本身内引用该变量。一个常见的例子是递归计算数字的阶乘,如下面的代码所示:
lambda_factorial = lambda i:1 if i==0 else i*lambda_factorial(i-1) print(lambda_factorial(4)) # 4 * 3 * 2 * 1 = 12 * 2 = 24
代码说明
在lambda功能,通过它的可变分配,传递一个值(4),它的计算结果,如果是0,否则它返回当前值返回1(i
)*另外计算的值的lambda函数- 1( i-1
)。这个过程持续到传递的值递减到0( return 1
)。一个过程,可以看到:
Python-功能函数的使用(三)