首页 > 代码库 > 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-功能函数的使用(三)