Python学习笔记九:函数

函数是组织好的、可重复使用的,用来实现单一或相关联功能的代码段。

函数能够提高应用的模块性和代码的重复利用率,Python提供了很多内建函数,如print()、int()等等。

1、定义函数

自定义函数的规则:

  • 函数代码块以def关键字开头,后接函数标识符名称和圆括号 “()”
  • 所有传入的参数和自变量都必须放在圆括号中,可以在圆括号中定义参数
  • 函数的第一行语句可以选择性使用文档字符串,用于存放函数说明;
  • 函数内容以冒号开始,并且要缩进;
  • return [表达式] 结束函数,选择性返回一个值给调用方,不带表达式的return相当于返回None
def 函数名 ():
     函数体

#或者更直观的表示为:

def <name> (arg1,arg2,…,argN)
     <statements>

函数名必须以字母开头,可以包括下划线“_”。和定义变量一样,不能把Python关键字定义成函数名称,函数内的语句数量是任意的,每个语句至少有一个空格的缩进,以表示该语句属于这个函数,函数体必须保持缩进一致,因为在函数中,缩进结束就表示函数结束。

2、调用函数

在程序设计中,函数是指用于进行某种计算的一系列语句的有名称的组合。定义函数时,需要指定函数的名称并编写一系列程序语句,之后可以使用名称“调用”这个函数。

函数括号中的表达式称之为函数的参数。函数“接收”参数,并“返回”结果,这个结果称为返回值。Python3内置了很多有用的函数,可以直接调用。要调用一个函数,就需要直到函数的名称和参数,比如求绝对值得函数abs只有一个参数。

可以直接从Python的官网查看文档,或者在交互式命令行通过help()查看函数的帮助信息。
函数名其实是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个别名。

函数的目的是把一些复杂操作隐藏起来,用于简化程序的结构,是程序更容易阅读。函数在调用前必须先定义。

3、装饰器

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

>>> def now():
...     print('2015-3-25')
...
>>> f = now
>>> f()
2015-3-25

函数对象有一个__name__属性,可以拿到函数的名字:

>>> now.__name__
'now'
>>> f.__name__
'now'

现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:

@log
def now():
    print('2015-3-25')

调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:

>>> now()
call now():
2015-3-25

把@log放到now()函数的定义处,相当于执行了语句:

now = log(now)

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

这个3层嵌套的decorator用法如下:

@log('execute')
def now():
    print('2015-3-25')

执行结果如下:

>>> now()
execute now():
2015-3-25

和两层嵌套的decorator相比,3层嵌套的效果是这样的:

>>> now = log('execute')(now)

我们来剖析上面的语句,首先执行log(‘execute’),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的’now’变成了’wrapper’:

>>> now.__name__
'wrapper'

因为返回的那个wrapper()函数名字就是’wrapper’,所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

或者针对带参数的decorator:

import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

import functools是导入functools模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。

4、函数的参数

调用函数时可以使用以下参数类型:

1)必须参数

必须参数必须以正确的顺序传入函数,调用时数量必须和声明时一样。

不传入或传入数量不对时,都会报错。所以对于此类函数,必须传递对应正确个数的参数。

2)关键字参数

关键字参数和函数调用关系紧密,函数调用使用关键字参数确定传入的参数值。

使用关键字参数允许调用函数时参数的顺序与声明时不一致,因为Python解释器能够用参数名匹配参数值。

def personinfo(age , name) :
   print(‘年龄:’ , age)
   print(‘姓名:’ , name)
   return

personinfo(21, ‘小萌’)
personinfo(name = ‘小萌’ , age = 21)
personinfo(age = 21 , name = ‘小萌’)

年龄:21
姓名:小萌

年龄:21
姓名:小萌

年龄:21
姓名:小萌

由以上输出结果可以看到,对于personinfo()函数,只要指定参数名,输入参数的顺序对结果就没有影响,都能得到正确的结果。

3)默认参数

调用函数时,如果没有传递参数,就会使用默认参数。

使用默认参数,就是在定义函数时,给参数一个默认值,如果没有给调用的函数的参数赋值,调用的函数就会使用这个默认值。

def defaultparam(name,age=23):
     print(‘hi ,我叫:’ , name)
     print(‘我今年:’ , age)
     return

defaultparam(‘小萌’)

hi,我叫:小萌
我今年:23

defaultparam(‘小萌’,21)

hi,我叫:小萌
我今年:21

由示例我们可以看到,在函数调用时没有对age赋值,在输出结果中使用了函数定义时的默认值,如果我们队age赋值,执行结果使用的是我们传入的参数。

当对默认参数传值时,函数执行时调用的是我们传入的值。

def defaultparam(age=23,name):
     print(‘hi ,我叫:’ , name)
     print(‘我今年:’ , age)
     return

defaultparam(age=21,name= ‘小萌’)

SyntaxError:non-default argument follows default argument

这里提醒我们,默认参数一定要放在非默认参数后面。

  • 无论有多少默认参数,默认参数都不能在必须参数之前;
  • 无论有多少默认参数,若不传入默认参数值,则使用默认值;
  • 若要更改一个默认参数值,又不想传入其他默认参数,且该默认参数的位置不是第一个,则可以通过参数名更改想要更改的默认参数值;
  • 若有一个默认参数通过传入参数名更改参数值,则其他想要更改的默认参数都需要传入参数名更改参数值,否则报错;
  • 更改默认参数时,传入默认参数的顺序不需要根据定义的函数中的默认参数的顺序传入,不过最好同时传入参数名,否则容易出现执行结果与与其不一致的情况。

通过实例可以看出,默认参数是比较有用的,通过默认参数可以帮助我们少写不少代码,不过使用默认参数是还是需要小心谨慎的。

4)可变参数

如果需要一个函数能够处理的参数声明时更多,这些参数叫做可变参数。与前面讲述的不同,可变参数声明时不会重名。

def functionname( [formal_agrs , ]  *var_args_tuple ):
     ‘函数_文档字符串’
     function_suite
     return  [expression]

加了星号 (*) 的变量名会存放所有未命名的变量参数。如果变量参数在函数调用时没有指定参数,就是一个空元组。我们也可以不向可变函数传递未命名的变量。

def personinfo(agr,*vartuple):
     print(arg)
     for var in vartuple:
         print(‘我属于不定长参数部分:’ , var)
     return

personinfo(‘小萌’)
personinfo(‘小萌’,21, ‘beijing’)
personinfo(‘小萌’,21, ‘beijing’ ,123, ‘shanghai’ , ‘happy’)

小萌

小萌
我属于不定长参数部分:21

小萌
我属于不定长参数部分:21
我属于不定长参数部分:beijing
我属于不定长参数部分:123
我属于不定长参数部分:shanghai
我属于不定长参数部分:happy

这段代码看起来很不可思议,在定义函数时只定义了两个参数,调用时却可以传入那么多参数。这其实就是可变参数的好处,我们在参数前面加了一个星号,在函数内部,参数前的星号将所有值都放在同一个元组中,通过这种方式将这些值收集起来,然后使用。参数vartuple接收的是一个元组,调用时可以传入任意个数的参数,也可以不传。

other = {‘城市’: ‘北京’ , ‘ 爱好’ : ‘编程’}
def personinfo(name , number , **kw):
     print(‘名称:’ ,name , ‘学号:’ , number , ‘其他:’ ,kw)

personinfo(‘小智’ , 1002 , 城市 = other[‘城市’] , 爱好 = other[‘爱好’])

名称:小智  学号 : 1002  其他:{‘爱好’ : ‘编程’ , ‘城市’: ‘北京’}

由函数执行结果看到,可以使用两个“”号,即使用“*”处理关键字参数。函数调用时可以用更简单的方式调用,简单形式如下:

personinfo(‘小智’ , 1002 , **other)

名称:小智  学号 : 1002  其他:{‘爱好’ : ‘编程’ , ‘城市’: ‘北京’}

执行结果和前面一样,写法上却简单了很多。此处**other表示把other这个字典的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个字典,注意kw获得的字典是other复制的,对kw的改动不会影响函数外的other。

5)组合参数

在Python中定义函数可以使用必须参数、关键字参数、默认参数和可变关键字参数,这四种参数还可以组合使用。注意定义参数的顺序必须是必须参数、默认参数、可变参数和关键字参数。

def exp(p1 , p2 ,df = 0 , *vart , **kw)
    print(‘p1’ , p1 , ‘p2’ , p2 , ‘df=’ , df , ‘vart=’ , vart , ‘kw=’,kw)

exp(1,2)
wxp(1,2,c=3)
exp(1,2,3, ‘a’ , ‘b’)
exp(1,2,3, ‘abc’ ,x=9)

p1 = 1  p2 = 2 df = 0  vart=()  kw = {}
p1 = 1  p2 = 2 df = 0  vart=()  kw = {‘c’:3}
p1 = 1  p2 = 2 df = 3  vart=(‘a’ , ‘b’)  kw = {}
p1 = 1  p2 = 2 df = 0  vart=(‘abc’,)  kw = {‘x’ :9 }

由输出结果看到,使用了组合参数,在调用函数时,Python解释器会自动按照参数位置和 参数名把对应的参数传进去。

6)形参与实参

在使用一些内置函数时需要传入参数,有些需要传入一个参数,有的函数需要多个参数。
在函数内部,会将实参的值赋值给形参,

def persioninfo(age , name):
     print(‘年龄:’ , age)
     print(‘名称:’ , name)
     return

在该函数中,函数名persioninfo后面的参数列表age和name就是实参,在函数体中分别将age和name的值传递给age和name函数体中的age和name就是形参。

注:
在函数护体内都是对形参进行操作,不能操作实参,即对实参作出更改。

内置函数的组合规则在自定义函数上同样适用,在Python中,作为实参的表达式会在函数调用前执行,作为实参传入函数的变量名称和函数定义里形参的名字没有关系,函数只关心形参的值,而不关心它在调用前叫什么。

5、变量的作用域

在python中,程序的变量并不是在任何位置都可以访问的,访问权限决定了这个变量是在哪里赋值的,代码中变量被赋值的位置决定哪些范围的对象可以访问这个变量,这个范围就是命名空间。

变量的作用域决定那一部分程序可以访问特定的变量名称。Python中有两种最基本的变量作用域:局部变量和全局变量。

1)局部变量

在函数内定义的变量名只能被函数内部引用,不能再函数外引用,这个变量的作用域就是局部的,也称为局部变量。

定义的变量如果是在函数体内第一次出现,就是局部变量;

局部变量只能在函数体中被访问,超出函数体的范围访问就会报错;

在函数体中可以直接使用函数体外的变量;

在函数体中更改变量的值并不会更改函数体外变量的值。这是因为调用函数时创建了新的命名空间,它作用于函数的代码块。赋值语句在函数体的作用域内起作用,不影响外部作用域的变量。

2)全局变量

在函数外,一段代码最开始赋值的变量可以被多个函数引用,这就是全局变量。全局变量可以在整个程序范围内访问。

全局变量可在全局使用,在函数体中更改全局变量的值不会影响全局变量都在其他函数或语句中的使用。

函数中使用某个变量时,如果该变量名既有全局变量又有局部变量,就默认使用局部变量。
要将全局变量变为局部变量,只需在函数中定义一个和局部变量名称一样的变量即可。

num = 100
print(‘函数调用前num的值为:’ , num)
def funs():
     global num
     num = 200
     print(‘函数体内的num的值是:’ , num)

func()
print(‘函数调用结束后num的值为:’ , num)

函数调用前num的值为:100
函数体内的num的值是:200
函数调用结束后num的值为:200

由函数定义及执行结果看到,在函数体中的变量num前加一个global关键字后,函数调用结束后,在函数外使用num变量时,值变为和函数体中的值一样。

要在函数中将某个变量定义为全部变量,在需要被定义的变量前加一个关键字global即可。

3)global 和 nonlocal

当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了。
以下实例修改全局变量 num:

#!/usr/bin/python3

num = 1
def fun1():
    global num  # 需要使用 global 关键字声明
    print(num) 
    num = 123
    print(num)
fun1()
以上实例输出结果:
1
123

如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了,如下实例:

#!/usr/bin/python3

def outer():
    num = 10
    def inner():
        nonlocal num   # nonlocal关键字声明
        num = 100
        print(num)
    inner()
    print(num)
outer()
以上实例输出结果:
100
100

另外有一种特殊情况,假设下面这段代码被运行:

#!/usr/bin/python3

a = 10
def test():
    a = a + 1
    print(a)
test()
以上程序执行,报错信息如下:
Traceback (most recent call last):
  File "test.py", line 7, in <module>
    test()
  File "test.py", line 5, in test
    a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment

错误信息为局部作用域引用错误,因为 test 函数中的 a 使用的是局部,未定义,无法修改。

6、函数返回值

若定义函数时没有使用return语句,则默认返回一个None。要返回一个None,可以只写一个return。

但要返回具体的数值,就需要在return后面机上需要返回的内容。

对于函数的定义来说,使用return语句可以向外提供该函数执行的一些结果;对于函数的调用者来说,是否可以使用函数中执行的一些操作结果,就在于函数是否使用return语句返回了对应的执行结果。

在Python中,有的函数会产生结果,我们称这种函数为有返回值函数;有的函数执行一些动作后不反悔任何值,我们称这类函数为无返回值函数。

当我们调用有返回值函数时,可以使用返回的结果作相关操作;当我们使用无返回值或返回None的函数时,只能得到一个None值。

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

我们来实现一个可变参数的求和。通常情况下,求和的函数是这样定义的:

def calc_sum(*args):
    ax = 0
    for n in args:
        ax = ax + n
    return ax

但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:

>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>

调用函数f时,才真正计算求和的结果:

>>> f()
25

在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
请再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False

f1()和f2()的调用结果互不影响。

1)闭包

注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。

另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()

在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:

>>> f1()
9
>>> f2()
9
>>> f3()
9

全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    return fs

再看看结果:

>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9

缺点是代码较长,可利用lambda函数缩短代码。

7、递归函数

如果一个函数在内部调用自身,这个函数就称作递归函数。

def recurision():
    return recurision()

这类递归被称作无穷递归,理论上永远不结束,当然,我们需要能够实际做事情的函数,有用的递归函数应该满足如下条件:

  • 当函数直接返回值时有基本实例(最下可能性问题);
  • 递归实例,包括一个或多个问题最小部分的递归调用;

使用递归关键在于将问题分解为小部分,递归不能永远继续下去,因为它总是以最小可能性问题结束,而这些问题又存储在基本实例中。

其实函数每次被调用时都会创建一个新命名空间,也就睡当函数调用“自身”时,实际上运行的是两个不同的函数(也就是说一个函数具有两个不同的命名空间)。

计算阶乘n!=1*2*3*…*n,用函数fact(n)表示,可以看出:

fact(n)=n!= 18283*…*(n-1)*n = (n-1)! * n = fact(n-1)*n

所以,fact(n)可以表示为n*fact(n-1),只有n=1时需要特殊处理。
于是,fact(n)用递归方式定义函数为:

def fact(n)
    if n ==1:
       return 1
    return  n*fact(n-1)

由函数定义得知,递归函数的优点是定义简单、逻辑清晰。

理论上,所有递归函数都可以写成循环的方式,不过循环的逻辑不如递归清晰。

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈这种数据结构实现的。每当进入一个函数调用,栈就会加一层栈帧;每当函数返回,栈就会减少一层栈帧。由于栈的大小不是无限的,因此递归调用的次数过多会导致栈溢出。

解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果一样,把循环看成一个特殊尾递归函数也可以。

尾递归是指在函数返回时调用函数本身,并且return语句不能包含表达式。这样,编译器或解释器就可以对尾递归进行优化,使递归本身无论调用多少次都只占用一个栈帧,从而避免栈溢出的情况。

由于上面的fact(n)函数return n*fact(n-1)引入了乘法表达式,因此不是尾递归。要改成尾递归方式需要多一点代码,主要是把每一步乘积传入递归函数中:

def fact(n):
     return fact_iter(n,1)

def fact_iter(num , product):
     if num == 1:
         return product
      return fact_iter(num -1 , num * product)

可以看到,return fact_iter(num -1 , n * product)仅返回递归函数本身,num-1和num*product在函数调用前就会被计算,不影响函数调用。

由操作结果看到,调用尾递归时入股做了优化,栈就不会增长,因此无论多少次调用都不会导致栈溢出。

8、匿名函数

匿名函数就是不再使用def语句这样的标准形式定义一个函数。

Python使用lambda创建匿名函数

lambda只是一个表达式,函数体比def简单很多

lambda的主体是一个表达式,而不是一个代码块,仅能在lambda表达式中封锁有限的逻辑。lambda函数拥有自己的命名空间,不能访问自由参数列表之外或全局命名空间的参数。

lambda函数的语法只包含一个语句:

lambda [arg1 [,arg2,…argn]]:expression

一般以下情况多考虑使用匿名函数:

  • 程序一次性使用、不需要定义函数名时,用匿名函数可以节省内存中变量定义空间。
  • 如果想让程序更加简洁,使用匿名函数就可以做到

匿名函数的3个规则:

  • 不能有return;
  • 一般有一行表达式,必须有返回值;
  • 可以没有参数,也可以有一个或多个参数;

如下实例:

#!/usr/bin/python3

sum = lambda arg1, arg2: arg1 + arg2;

# 调用sum函数
print ("相加后的值为 : ", sum( 10, 20 ))
print ("相加后的值为 : ", sum( 20, 20 ))
以上实例输出结果:
相加后的值为 :  30
相加后的值为 :  40

9、偏函数

偏函数是将所有承载的函数作为partial()函数的第一个参数,原函数的各个参数以此作为partial()函数的后续参数,除非使用关键字参数。

Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。要注意,这里的偏函数和数学意义上的偏函数不一样。

在介绍函数参数的时候,我们讲到,通过设定参数的默认值,可以降低函数调用的难度。而偏函数也可以做到这一点。举例如下:

int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换:

>>> int('12345')
12345

但int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换:

>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565

假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去:

def int2(x, base=2):
    return int(x, base)

这样,我们转换二进制就非常方便了:

>>> int2('1000000')
64
>>> int2('1010101')
85

functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
注意到上面的新的int2函数,仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值:

>>> int2('1000000', base=10)
1000000

最后,创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数,当传入:

int2 = functools.partial(int, base=2)

实际上固定了int()函数的关键字参数base,也就是:

int2('10010')

相当于:

kw = { 'base': 2 }
int('10010', **kw)

当传入:

max2 = functools.partial(max, 10)

实际上会把10作为*args的一部分自动加到左边,也就是:

max2(5, 6, 7)

相当于:

args = (10, 5, 6, 7)
max(*args)

结果为10

当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

猜你喜欢

转载自blog.csdn.net/viatorsun/article/details/80218428
今日推荐