函数
参数
默认参数
在函数声明的参数列表中的参数后面加入等于号设置默认值.
注意
默认参数必须指向不可变对象, 如果指向可变对象, 在第一次调用时修改该参数, 由于默认参数赋值的时候只存储的是这个对象的地址, 所以下一次调用该函数的时候, 其地址虽然没变, 但是值已经改变了.
可变参数
在函数声明的参数列表中还可以定义可变参数, 可变参数的作用就是使传入参数的个数可变. 在有可变参数存在的情况下调用函数, 首先会匹配定义死的参数, 剩余的参数会以元组的形式存储在可变参数中. 使用*
表示可变参数.
Tip
如果要传入一个列表里面的值作为可变参数怎么办? 可以在列表或者元组前面加一个*
, 把列表或者元组的元素变成可变参数传进去:
关键字参数
在函数声明的参数列表中还可以定义关键字参数, 关键字参数的作用类似于可变参数, 但是, 传入的必须是键值对. 在有关键字参数存在的情况下调用函数, 首先会匹配定义死的参数, 剩余的参数会以字典的形式存储在关键字参数中. 使用**
表示关键字参数.
Tip
如果要传入一个字典里面的值作为关键字参数怎么办? 可以在字典前面加一个**
, 把字典的键值对变成关键字参数传进去:
命名关键字参数
对于关键字参数, 函数的调用者可以传入不受限制的关键字参数. 如果要限制关键字参数的名字, 就可以使用命名关键字参数. 命名关键字阐述需要一个分隔符*
, *
后面的参数被视为命名关键字参数.
例子
定义函数:
调用函数:
Tip
如果函数定义中已经有了一个可变参数, 后面跟着的命名关键字参数就不再需要一个分隔符*
了
注意
调用函数时必须传入所有的命名关键字参数, 不然会报错. 如果有默认值, 可以通过设置默认值的方式在调用时缺省.
例子
定义函数:
调用函数:
参数组合
在定义函数时, 可以选用必选参数, 默认参数, 可变参数, 关键字参数和命名关键字参数, 这5中参数都可以组合使用. 但是请注意, 参数定义的顺序一定是: 必选参数, 默认参数, 可变参数, 命名关键字参数和关键字参数.
例子
定义函数:
调用函数:
>>> f1(1, 2)
a= 1 b = 2 c = 0 args = () kw = {}
>>> f1(1, 2, c=3)
a = 1 b = 2 c = 3 args = () kw = {}
>>> f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>> f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
>>> t = (1, 2, 3, 4)
>>> d = {'d': 99, 'x': '#'}
>>> func1(*t, **d)
a=1 b=2 c=3 args=(4,) kw={'d': 99, 'x': '#'}
类型提示
Python3.6+ 版本加入了对"类型提示"的支持.
通过声明变量的类型, 编辑器和一些工具能给你提供更好的支持, 而且不会改变函数的运行结果.
参数类型提示8
简单类型
所有的标准Python模型都可以被声明. 比如以下类型:
str
int
float
bool
bytes
例子
嵌套类型
容器可以包含值, 容器本身拥有类型, 容器内部的这些值也拥有自己的类型. 可以使用typing
模块来声明这些类型以及子类型.
列表, 元组或集合
按照需求, 从typing
模块导入List
, Tuple
或Set
.
Tip
还可以为特定位置上的元素指定类型.
字典
从typing
模块导入Dict
. 定义时需要传入两个子类型, 用逗号分隔, 第一个子类型声明字典的所有键的类型; 第二个子类型声明字典的所有值的类型.
例子
类作为类型
可以声明将类作为变量的类型.
例子
返回值类型提示9
返回值类型提示和参数类型提示的写法类似, 只不过放在->
后面.
例子
Tip
Optional[[type]]
表示某个变量可以是指定类型或者None
-
若函数可能返回多个不同类型的值, 可以使用
typing
模块中的Union
类来指定多个返回类型.
高阶函数
函数可以作为参数传给另一个函数, 这种函数就称为高阶函数.
笔记
-
变量也可以指向函数
-
函数名也是变量
map()
map()
函数接收两个参数, 一个是函数(接收一个参数, 返回一个值), 一个是可迭代对象(Iterable), map()
将传入的函数依次作用到序列的每一个元素, 并把结果作为新的迭代器(Iterator)返回.
例子
Tip
可以通过list()
函数把迭代器(惰性序列)都计算出来并返回一个列表.
reduce()
reduce()
函数接收两个参数, 一个是函数(接收两个参数, 返回一个值), 一个是可迭代对象(Iterable), reduce()
首先会移除序列的前两个元素, 将传入的函数依次作用到这两个元素, 然后将返回值放回到序列中.(1) 最终返回一个单一的值 (特别适合做累计计算).
- 便于方便理解, 原理是否是这样的有待考证.
注意
要使用reduce()
, 必须先导包from functools import reduce
.
例子
filter()
filter()
函数接收两个参数, 一个是函数(接收一个参数, 返回True/False), 一个是可迭代对象(Iterable), filter()
将传入的函数依次作用于序列的每一个元素, 选择保留或者丢弃该元素, 并把结果作为新的迭代器(Iterator)返回.
例子
>>> def is_odd(x):
... return n % 2 == 1
...
>>> list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
[1, 5, 9, 15]
Tip
可以通过list()
函数把迭代器(惰性序列)都计算出来并返回一个列表.
sorted()
sorted()
函数接收两个参数, 一个是可迭代对象(Iterable), 一个是函数(接收一个参数, 返回一个值). 这个函数要以关键字参数的形式传入. 并把结果作为新的可迭代对象(Iterable)返回.
Tip
还有一个函数, sort()
也能实现类似的功能, 但是sort()
是对本身进行排序, 如lis.sort()
, 而sorted()
会返回排序后的对象.
返回函数
函数可以作为结果值返回, 这就叫做返回函数.
例子
定义函数:
调用函数:
闭包
闭包, 指的就是内层函数使用了外层函数的局部变量, 虽然外层函数返回内层函数时它的生命周期已经完成, 外层函数的局部变量已经被销毁, 但是内层函数能够记住它被创建时的环境, 即使在它被调用时, 这些环境已经不再存在.
例子
定义函数:
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i))
return fs
调用函数:
f(i)
会立即执行, 此时, f(j)
函数内部的j
不是一个循环变量, 而是一个确定量, 所以g
里面引用的j
也是确定的, 就是此时的i
的值.
注意
返回函数不要引用任何循环变量, 或者在外层函数在执行过程中会发生变化的量. 否则返回函数中的这个变量的值是外层函数对应变量的最后一个值.
nonlocal
如果我们仅仅只是读取引用的外层变量的值, 一切正常. 但是, 如果我们想在内层函数中改变引用的外层变量, 我们需要用到nonlocal
关键字. 如果不声明的话, 会把该变量当成内层函数的局部变量, 导致报错.
例子
定义函数:
调用函数:
Lambda函数
在传入函数的时候, 不需要显式定义函数, 直接传入匿名函数更加方面, 在Python中, Lambda函数表示的就是匿名函数. 其基本语法为:
笔记
-
匿名函数也是一个函数对象, 也可以把匿名函数赋值给一个变量, 再利用变量来调用该函数:
-
可以把匿名函数作为返回值返回:
装饰器
装饰器用于增强函数的功能. 有时我们不希望修改函数的定义, 但是希望在代码运行期间动态增加函数的功能, 实现这种效果的工具就叫做装饰器.
不带参数的装饰器
若装饰器没有参数, 那么其作用为使得我们在调用被装饰函数的时候, 执行的是装饰后的函数.
例子
定义函数:
def log(func):
def wrapper(t):
print('%s():' % (func.__name__))
print("i am", t)
return func(t)
return wrapper
@log
def now(text):
print(text)
调用函数:
解释:
- 对
now(text)
函数做一点修饰, 新的now
函数为log(now(text))
log(now(text))
会返回一个wrapper(t)
函数, 即新的now(text)
函数为wrapper(t)
函数- 当我们执行
now("wenzexu")
的时候, 实际执行的是wrapper("wenzexu")
带参数的装饰器
若装饰器带有参数, 这个时候情况有点复杂, 用例子解释:
例子
定义函数:
def log(param):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (param, func.__name__))
print("I am", args[0])
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now(text):
print(text)
调用函数:
解释:
- 对
now(text)
函数做一点修饰, 新的now(text)
函数为log(param)(now(text))
log(param)
会返回一个decorator(func)
函数, 而decorator(now)
会返回一个wrapper(*args, **kw)
函数, 即新的now(text)
函数为wrapper(*args, **kw)
函数- 当我们执行
now("wenzexu")
的时候, 实际执行的是wrapper("wenzexu")
函数签名
注意
上述两种的装饰器定义没有问题, 但还差一步, 经过装饰之后的函数其签名即__name__
属性会改变, 变为实际执行的装饰函数的__name__
. 这有可能导致依赖函数签名的代码执行报错, 例子:
例子
定义函数:
def log(func):
def wrapper(t):
print('%s():' % (func.__name__))
print("i am", t)
return func(t)
return wrapper
@log
def now(text):
print(text)
测试函数:
为了解决这个问题, 我们无需编写wrapper.__name__ = func.__name__
这样的代码, 只需要用内置的functools.wraps
:
注意
使用functools.warps
需要导入functools
模块.
例子
定义函数:
import functools
def log(func):
@functools.wraps(func)
def wrapper(t):
print('%s():' % (func.__name__))
print("i am", t)
return func(t)
return wrapper
@log
def now(text):
print(text)
测试函数:
偏函数
偏函数的作用是把一个函数的某些参数固定住, 返回一个新的函数, 调用这个新函数会更简单.
注意
偏函数依赖于functools
模块, 需要导入该模块.
-
函数的参数. (n.d.). Retrieved June 13, 2024, from https://www.liaoxuefeng.com/wiki/1016959663602400/1017261630425888 ↩
-
n3xtchen. (2014, August 8). Python 优雅的使用参数—可变参数(args & *kwargs). https://n3xtchen.github.io/n3xtchen/python/2014/08/08/python-args-and-kwargs/ ↩
-
高阶函数. (n.d.). Retrieved June 14, 2024, from https://www.liaoxuefeng.com/wiki/1016959663602400/1017328655674400 ↩
-
返回函数. (n.d.). Retrieved June 14, 2024, from https://www.liaoxuefeng.com/wiki/1016959663602400/1017434209254976 ↩
-
匿名函数. (n.d.). Retrieved June 14, 2024, from https://www.liaoxuefeng.com/wiki/1016959663602400/1017451447842528 ↩
-
装饰器. (n.d.). Retrieved June 14, 2024, from https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584 ↩
-
偏函数. (n.d.). Retrieved June 14, 2024, from https://www.liaoxuefeng.com/wiki/1016959663602400/1017454145929440 ↩
-
Python 类型提示简介—FastAPI. (n.d.). Retrieved June 22, 2024, from https://fastapi.tiangolo.com/zh/python-types/ ↩
-
Python 如何使用类型提示指定多个返回类型|极客教程. (n.d.). Retrieved June 22, 2024, from https://geek-docs.com/python/python-ask-answer/472_python_how_to_specify_multiple_return_types_using_typehints.html ↩