Python/函数

维基教科书,自由的教学读本
跳到导航 跳到搜索

函数定义[编辑]

Python函数定义的通常格式为:

def 函数名参数列表:
    #函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明
    函数体
    #return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None

参数的传值与传引用[编辑]

Python中,对象有类型;变量仅仅是对象的一个引用,变量没有类型。 string、tuple和 numbers 是不可更改的类型(immutable type),给它们的变量赋值其实是指向了新的对象;而 list,dict 等则是可以修改的类型(mutable type)。Python函数的参数传递

  • 不可变类型:类似 C语言的值传递。在函数内部“修改”参数的值,只是引用到另一个对象,不影响实参本身。
  • 可变类型:类似C++的引用传递,修改后函数外部的实参也会受影响

命名实参与不定长参数[编辑]

Python的函数实参可分为两类:(无命名的)位置实参、命名实参。

  • 函数调用时,首先匹配所有位置参数,即使关键字参数更靠左也会稍后才处理。额外的未匹配的位置参数组成一个元组与*args形参匹配。
  • 一个星号*为前缀的形参名字,对应于匹配了位置形参与命名形参后剩下的额外位置参数,组成的一个元组。
    • 单独一个星号没有形参名字,只是一个分隔符,并不对应于实参,而是表示实参表此处向右都必须是命名参数。出现在*args或者**kwargs之后的形参都是命名参数。
  • 两个星号*为前缀的形参名字,对应于额外的命名实参组成的一个字典。
  • 带缺省值的参数,其右侧直至*的参数都必须有缺省值。参数的缺省值在函数定义时从左至右求值。

通常,不在一个函数同时使用*args与**kwargs,以免混淆。

函数调用时,如何传入任意多的实参? 使用“逆向参数收集”。即在list变量名字前面加上*号作为实参;tuple变量名字前面加上**号作为实参。

def test1(a, b, c=0, *args, **kwargs):
    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kwargs)
 
def test2(a, b, c=0, *args, d, **kwargs):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'args=', args, 'kw =', kwargs)
 
# 定义一个元组和字典用作参数传入
args = (1, 2, 3, 4)
kw = {'d': 99, 'x': '#'}
 
test1(*args, **kw)
# a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
test2(*args, **kw)
#a = 1 b = 2 c = 3 d = 99 args= (4,) kw = {'x': '#'}

def test3(a, *args,d, c=0):
    print('a =', a, 'c =', c, 'd =', d, 'args=', args)
args=(1,3,5)
test3(-1, d=101, *args)
#输出为:a = -1 c = 0 d = 101 args= (1, 3, 5)

函数返回多个值[编辑]

返回多个值时自动作为一个tuple。函数调用者可以使用多个变量来接受多个返回值(即tuple中的各个成分)。

嵌套函数[编辑]

嵌套函数(nested function),需要注意:

  • 内部函数可以读写访问外部函数的可变(mutable)变量
  • 内部函数可以访问外部函数的不可变(immutable)变量
  • 内部函数使用nonlocal关键字,可以读写访问直接外部函数的不可变(immutable)变量
  • 内部函数访问的外部函数的变量,可以定义在内部函数之后。

函数闭包[编辑]

闭包是指嵌套函数作为外部函数的返回值。因为函数是头等对象,所以函数对象可以作为实参、返回值。嵌套函数可以使用外部函数的变量,即使外部函数已经运行结束了。例如

 def adder(outer_argument): # outer function
   def adder_inner(inner_argument): # inner function, nested function
     return outer_argument + inner_argument # Notice outer_argument
   return adder_inner
 add5 = adder(5) # a function that adds 5 to its argument
 add7 = adder(7) # a function that adds 7 to its argument
 print add5(3) # prints 8
 print add7(3) # prints 10

匿名函数[编辑]

匿名函数的语法如下:

 lambda 可选的形参表:expression

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

偏函数[编辑]

对一个函数的部分参数绑定值后,得到的函数为偏函数:

偏函数名 = partial(func, *args, **kwargs)

其中,func 指的是要封装的原函数,*args 和 **kwargs 分别用于接收非命名实参和命名实参。

from functools import partial

#定义个原函数
def display(name,age):
    print("name:",name,"age:",age)

#定义偏函数,其封装了 display() 函数,并为 name 参数设置了默认参数
GaryFun = partial(display,name = 'Gary')
#由于 name 参数已经有默认值,因此调用偏函数时,可以不指定
GaryFun(age = 13) #必须以命名实参方式传入,否则会报错:TypeError: display() got multiple values for argument 'name'

def mod( n, m ):
    return n % m

#定义偏函数,并设置参数 n 对应的实参值为 100
mod_by_100 = partial( mod, 100 )
print(mod( 100, 7 ))
print(mod_by_100( 7 ))

eval()与exec()[编辑]

eval(source, globals=None, locals=None, /)
exec(source, globals=None, locals=None, /)

expression参数是一个字符串,代表要执行的语句 。该语句受后面两个字典类型参数 globals 和 locals 的限制,只有在 globals 字典和 locals 字典作用域内的函数和变量才能被执行。

globals参数管控的是一个全局的命名空间,即 expression 可以使用全局命名空间中的函数。如果只是提供了 globals 参数,而没有提供自定义的 __builtins__,则系统会将当前环境中的 __builtins__ 复制到自己提供的 globals 中,然后才会进行计算;如果连 globals 这个参数都没有被提供,则使用 Python 的全局命名空间。

locals参数管控的是一个局部的命名空间,和 globals 类似,当它和 globals 中有重复或冲突时,以 locals 的为准。如果 locals 没有被提供,则默认为 globals。

eval() 执行完会返回结果,而 exec() 执行完不返回结果

map()、filter()、reduce()[编辑]

由于 map() 函数是直接由用 C 语言写的,运行时不需要通过 Python 解释器间接调用,并且内部做了诸多优化,所以相比其他方法,此方法的运行效率最高。

reduce() 函数在 Python 3.x 中已经被移除,放入了 functools 模块,因此在使用该函数之前,需先导入 functools 模块。

函数注解[编辑]

“函数注解是关于用户自定义函数使用类型的可选的元信息”。也就是说,函数注解的用途为“函数中的形参和返回值提供类型提示信息”。注意,函数注解没有任何语法上的意义,只是为函数参数和返回值做注解,并在运行获取这些注解,仅此而已。换句话说,为函数做的注解,Python不做检查,不做强制,不做验证,什么操作都不做,函数注解对Python解释器没任何意义。

    def f(ham:str,egg:str='eggs')->str:
      pass
    print(f.__annotations__)

#输出结果为:{'ham': <class 'str'>, 'egg': <class 'str'>, 'return': <class 'str'>}