本篇博客为你带来 python 函数相关知识点的回顾。
函数是 python 语言中一个非常重要的概念,并且是头等对象,可以把函数分配给变量,也可以将其作为参数传递给其它函数,当然作为其它函数的返回值也是可以的。
函数就是对象
函数是对象,所以可以将其赋值给一个变量
def func(): print("hello 橡皮擦")
my_var = func # 注意没有括号my_var() # 调用
复制代码
函数是对象,所以可以出现在其它数据结构中,例如列表函数可以作为列表的项。
def func(): print("hello 橡皮擦")
my_list = [func, 1, 2, 3] # 列表第一项是函数print(my_list)my_list[0]() # 列表第一项可以调用
复制代码
函数是对象,所以可以作为其它函数的参数
def func(): print("hello 橡皮擦")
def func1(func): func() # 调用参数
func1(func) # 函数作为参数
复制代码
可以接受其它函数作为参数的函数,称为高阶函数,这与我们之前学习的函数式编程又产生了关联。
函数是对象,所以可以作为其它函数的返回值
函数作为返回值,又衍生出来一个概念,函数的嵌套(内部函数)。
def func(): def inner_func(): print("内部函数")
return inner_func
print(func()) # 输出 <function func.<locals>.inner_func at 0x00000000028581E0>
func()() # 输出内部函数
复制代码
如果函数作为返回值,那调用外层函数只能得到一个函数对象,因此会出现 func()() 这样奇怪的写法。
函数的嵌套还衍生一个概念,叫做 词法闭包,一般叫做 闭包,其核心内容是内部函数可以访问父函数中的参数。
def func(): f_name = "橡皮擦"
def inner_func(): print("内部函数") print(f_name)
return inner_func
inner = func()
inner() # 输出内部函数与橡皮擦
复制代码
python 中小小的装饰器
学习装饰器的前提是掌握 函数是头等对象。
装饰器是用来 包装 函数的,它可以在不修改原函数的前提下,在被包装函数的前后增加代码。
最简单的包装如下,给一个函数增加运行时间。
def func(): for i in range(100000): pass
def time_decorator(func): import time start_time = time.perf_counter() func() print(f"{func.__name__} 运行时间为:", time.perf_counter() - start_time)
# 装饰 func 函数time_decorator(func)
复制代码
上述代码中 time_decorator 就是一个装饰器,func 函数作为参数传递给它,从而实现代码运行时间的获取。
使用 @函数名 可以更加方便的调用装饰器。
def time_decorator(func): import time start_time = time.perf_counter() func() print(f"{func.__name__} 运行时间为:", time.perf_counter() - start_time)
@time_decoratordef func(): for i in range(100000): pass
复制代码
使用 @ 语言,会立即装饰该函数,如果你想要调用 func() ,此时会出现如下错误:
Traceback (most recent call last): File "E:/xxxx/18.py", line 13, in <module> func()TypeError: 'NoneType' object is not callable
复制代码
如果希望原函数还可以访问,需要在装饰器函数中嵌套内部函数,并将其返回。
def time_decorator(func): import time # 嵌套函数 def wrapper(): start_time = time.perf_counter() func() print(f"{func.__name__} 运行时间为:", time.perf_counter() - start_time) # 返回内部函数 return wrapper
@time_decoratordef func(): for i in range(100000): pass
func()
复制代码
一个函数可以被多个装饰器装饰,效果从下到上运行从下到上理解为接近函数的装饰器先运行。
# 加粗标签装饰器def b(func): def wrapper(): return f'<b>{func()}</b>'
return wrapper
# 段落标签装饰器def p(func): def wrapper(): return f'<p>{func()}</p>'
return wrapper
@p@bdef my_func(): return "橡皮擦"
print(my_func())
复制代码
装饰器由于词法闭包,会隐藏原函数的名称、文档字符串、参数列表为了解决该问题,需要导入 functools 模块中的 wraps 函数,先测试一下被装饰器修饰之后的函数相关信息。
def title(func): def wrapper(): """装饰器""" return func().title()
return wrapper
@titledef say(): return "hello world"
h = say()print(h) # 被装饰之后,单词首字母大写。print(say.__name__) # 输出其函数名print(say.__doc__) # 文档字符串
复制代码
对应的输出内容如下:
可以发现 say 函数的函数名与文档字符串都已经被修改,如果不希望上述现象产生,使用 functools 模块的 wraps 函数即可。
from functools import wrapsdef title(func): @wraps(func) # 复制信息到装饰器 def wrapper(): """装饰器""" return func().title()
return wrapper
@titledef say(): """say 函数文档字符串""" return "hello world"
h = say()print(h) # 被装饰之后,单词首字母大写。print(say.__name__) # 输出其函数名print(say.__doc__) # 文档字符串
复制代码
对应输出修改为下述内容:
Hello Worldsaysay 函数文档字符串
复制代码
装饰器中的参数在编写装饰器的时候,经常碰到被装饰的函数具有参数情况,实操过程中使用 python 变长参数 * 和 ** 特性,即可解决该问题。如果函数只有一个参数,直接在 wrapper 声明即可
def title(func): @wraps(func) # 复制信息到装饰器 def wrapper(name): """装饰器""" return func(name).title()
return wrapper
@titledef say(name): """say 函数文档字符串""" return name + "hello"
h = say("橡皮擦")print(h)
复制代码
*多个参数就比较繁琐了,所以位置参数直接使用 *args,关键字参数使用 *kwargs
from functools import wraps
def title(func): @wraps(func) # 复制信息到装饰器 def wrapper(*args): """装饰器""" return func(*args).title()
return wrapper
@titledef say(name, age): """say 函数文档字符串""" return name + " hello " + " age " + str(age)
h = say("橡皮擦",18)print(h)
复制代码
下述代码为关键字参数测试代码:
from functools import wraps
def title(func): @wraps(func) # 复制信息到装饰器 def wrapper(*args,**kwargs): """装饰器""" return func(*args,**kwargs).title()
return wrapper
@titledef say(name): """say 函数文档字符串""" return name + " hello "
h = say(name="橡皮擦")print(h)
复制代码
总结一下:
*args:用来收集额外的位置参数,组成元组;
**kwargs:用来收集关键字参数,组成字典。
其中 args 和 kwargs 非固定名称,你可以根据需要进行设置,不过行业里面其它程序员都遵守该规范。
评论