本篇博客为你带来 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_decorator
def 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_decorator
def 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
@b
def my_func():
return "橡皮擦"
print(my_func())
复制代码
装饰器由于词法闭包,会隐藏原函数的名称、文档字符串、参数列表为了解决该问题,需要导入 functools
模块中的 wraps
函数,先测试一下被装饰器修饰之后的函数相关信息。
def title(func):
def wrapper():
"""装饰器"""
return func().title()
return wrapper
@title
def say():
return "hello world"
h = say()
print(h) # 被装饰之后,单词首字母大写。
print(say.__name__) # 输出其函数名
print(say.__doc__) # 文档字符串
复制代码
对应的输出内容如下:
可以发现 say
函数的函数名与文档字符串都已经被修改,如果不希望上述现象产生,使用 functools
模块的 wraps
函数即可。
from functools import wraps
def title(func):
@wraps(func) # 复制信息到装饰器
def wrapper():
"""装饰器"""
return func().title()
return wrapper
@title
def say():
"""say 函数文档字符串"""
return "hello world"
h = say()
print(h) # 被装饰之后,单词首字母大写。
print(say.__name__) # 输出其函数名
print(say.__doc__) # 文档字符串
复制代码
对应输出修改为下述内容:
Hello World
say
say 函数文档字符串
复制代码
装饰器中的参数在编写装饰器的时候,经常碰到被装饰的函数具有参数情况,实操过程中使用 python 变长参数 *
和 **
特性,即可解决该问题。如果函数只有一个参数,直接在 wrapper
声明即可
def title(func):
@wraps(func) # 复制信息到装饰器
def wrapper(name):
"""装饰器"""
return func(name).title()
return wrapper
@title
def 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
@title
def 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
@title
def say(name):
"""say 函数文档字符串"""
return name + " hello "
h = say(name="橡皮擦")
print(h)
复制代码
总结一下:
*args
:用来收集额外的位置参数,组成元组;
**kwargs
:用来收集关键字参数,组成字典。
其中 args
和 kwargs
非固定名称,你可以根据需要进行设置,不过行业里面其它程序员都遵守该规范。
评论