Python-功能函数的使用(三),python-函数,使用可选的可变参数定


使用可选的可变参数定义函数

当使用有一个问题可选参数可变默认类型,这可能会导致意外行为。

说明

这个问题是因为函数的默认参数初始化一旦被该功能时,在点定义,并不能(像许多其他语言)当函数被称为的默认值存储在函数对象的内部__defaults__构件变量。

def f(a, b=42, c=[]):    passprint(f.__defaults__)# Out: (42, [])

对于不可改变的类型因为没有办法变异的变量,这是没有问题的;它只能被重新分配,使原始值不变。因此,后续保证具有相同的默认值。然而,对于一个可变类型,原始值可以发生变异,通过向它的各种部件的功能调用。因此,对函数的连续调用不能保证具有初始默认值。

def append(elem, to=[]):    to.append(elem)      # This call to append() mutates the default variable "to"    return toappend(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 againappend(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 = xx.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 = xx += [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 incincOne = makeInc(1)incFive = makeInc(5)incOne (5) # returns 6incFive(5) # returns 10

请注意,在常规闭包中,封闭函数完全从其封闭环境继承所有变量,在这个结构中,封闭函数只具有对继承变量的读访问权,但不能对它们进行赋值。

def makeInc(x):  def inc(y):     # incrementing x is not allowed     x += y       return x  return incincOne = 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 incincOne = makeInc(1)incOne(5) # returns 6

从函数返回值

函数可以return一个值,你可以直接使用:

def give_me_five():    return 5print(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 5first, second = give_me_two_fives()print(first)# Out: 5print(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):    passf(1, 2, 3)# TypeError: f() missing 1 required keyword-only argument: ‘b‘

在Python 3中,可以在函数签名中放置一个星号,以确保剩余的参数只能使用关键字参数传递。

def f(a, b, *, c):    passf(1, 2, 3)# TypeError: f() takes 2 positional arguments but 3 were givenf(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 1factorial(1)#out 1factorial(2)#out 2factorial(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=10):    return ‘{0} {1} {2}‘.format(value1, value2, optionalvalue1)

调用该函数时,您可以给每个关键字不带名称,但顺序很重要:

print(func(1, ‘a‘, 100))# Out: 1 a 100print(func(‘abc‘, 14))# abc 14 10

或者结合给出参数与名称和没有。然后名字的人必须遵循那些没有,但名称的顺序无关紧要:

print(func(‘This‘, optionalvalue=‘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 adderadd5 = make_adder(5)add6 = make_adder(6)add5(10)#Out: 15add6(10)#Out: 16def repeatedly_apply(func, n, x):    for i in range(n):        x = func(x)    return xrepeatedly_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-功能函数的使用(三)

评论关闭