「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」。
函数
Python 不是严格的面向对象语言,很多时候我们会选择面向函数的方式进行编程。那函数是什么呢?
我们可以把函数当作一个黑盒子,我们输入一些数据,函数会帮我们处理,然后返回一个结果。在 Python 中,函数通过 def 关键字定义,定义的格式如下:
def 函数名(参数列表):
函数体
return 返回值
其中参数列表和返回值不是必须的,但是如果函数体不执行任何操作,那我们需要用一个 pass 占位:
def func(a, b):
return a + b
上面我们定义了一个函数 func,参数为 a、b,返回值是 a 和 b 的和。在定义函数后,我们就可以使用它了:
def func(a, b):
return a + b
result = func(1, 2)
print(result)
相信上面这些知识大家都非常熟悉。在 Python 中,函数的定义是可以嵌套的,也就是说我们可以在函数里面定义函数:
def func(a, b):
def inner():
return 1
return a + b
在上面的代码我们在 func 里面定义了一个 inner 函数,这个时候我们需要注意,因为 inner 函数定义在 func 里面,所以我们只能在 func 里面使用 inner 函数,我们可以尝试运行下面的代码:
def func(a, b):
def inner():
return 1
return a + b
inner()
这个时候就会出现如下错误:
NameError: name 'inner' is not defined
意思就是 inner 没有定义,其实它的意思就是在当前作用域中没有定义。那我们有没有办法在全局使用局部定义的函数呢?
闭包
在 Python 中,函数也可以作为参数。我们可以执行下面的代码:
def func(a, b):
return a + b
print(func)
我们直接输出函数名,而没有加括号。输出结果如下:
<function func at 0x000002C83CC96678>
可以看懂结果是一个 function 的对象,我们可以把它赋值给另外的变量:
def func(a, b):
return a + b
my_func = func
print(my_func(1, 3))
我们把 func 赋值给 my_func 变量,然后发现 my_func 可以和 func 一样使用。输出结果如下:
4
结果和 func 的结果一样。那我们能不能把函数当做参数或者返回值呢?我们可以尝试一下:
def func():
def inner():
print("执行了 inner 函数")
return inner
in_func = func()
in_func()
我们在 func 里面定义了一个 inner 函数,按理我们是不能在外部调用 inner 函数的,但是我们把 inner 当作返回值返回到外部,这样我们就能在外部调用 inner 函数了。我们来看看运行结果:
执行了 inner 函数
可以看到确实执行了 inner 函数。其实 inner 函数我们就可以叫做闭包函数。那利用闭包,我们能干什么呢?我们可以利用闭包来实现装饰器。那装饰器又是什么呢?下面我们来详细看一下。
装饰器
装饰器如果从字面意思来讲就是用来装饰的东西,它装饰的对象是函数。我们可以用装饰器在不改变原函数的情况下对函数进行扩展。我们先不说装饰器,我们想想要怎么才能扩展一个函数,最简单的办法就是如下:
def func(a, b):
return a + b
print("在 func 之前执行")
func(1, 2)
print("在 func 之后执行")
我们直接调用这个函数,然后在函数前面添加一些代码,再在函数执行后面添加一些代码。这样很好的完成了我们的任务,但是这种方式属实有点矬略。如果我们经常要用到这种扩展,或者多个函数需要用到这样的扩展我们这种方式就束手无策了。
这个时候我们就可以使用闭包,我无法解释为什么要使用闭包,但是闭包能很好的解决这个问题。我们看下面这段代码:
def say_hi(func):
print("nice to meet you")
def inner(*args, **kwargs):
return func(*args, **kwargs)
print("good bye")
return inner
def func(a, b):
return a + b
say_hi_func = say_hi(func)
我们先定义一个函数 say_hi,它的参数是一个函数,也就是我们要扩展的函数。然后我们在里面定义一个 inner 函数,在 inner 函数中完成 func 函数的调用,并接收 func 的参数和返回值。最后将 inner 函数作为参数返回。
我们想对 func 函数进行扩展,我们只需要调用 say_hi(func),它就会给我们返回一个加强后的函数。我们可以执行看看:
def say_hi(func):
print("nice to meet you")
def inner(*args, **kwargs):
return func(*args, **kwargs)
print("good bye")
return inner
def func(a, b):
return a + b
say_hi_func = say_hi(func)
result = say_hi_func(1, 2)
print(result)
执行结果如下:
nice to meet you
good bye
3
可以看到我们成功扩展了 func 函数。但是这样有点混乱,我们还需要先加强函数才能使用,而另一种简单的方式就是使用 @ 符号:
def say_hi(func):
print("nice to meet you")
def inner(*args, **kwargs):
return func(*args, **kwargs)
print("good bye")
return inner
@say_hi
def func(a, b):
return a + b
print(func(1, 2))
我们在 func 函数定义的时候执行了添加了 @say_hi,这个时候 func 就是解释器会帮我们完成下面几句代码:
func = say_hi(func)
这样就不需要再使用一个新的函数了。
多装饰器
我们可以给一个函数添加多个装饰器,其实使用也是一样的。我们可以这样里面,假如我们有如下函数:
def func():
print("这是函数体")
假如我们有如下装饰器:
def decorate(func):
print("函数执行前")
def inner(*args, **kwargs):
return func(*args, **kwargs)
print("函数执行后")
return inner
在外面对 func 函数进行装饰后,func 的内容变为:
def func():
print("函数执行前")
print("函数体")
print("函数执行后")
这时使用装饰器后的函数和普通函数没有区别,外面依旧可以用同样的方式给他添加多个装饰器:
def dec1(func):
print("start dec1")
def inner(*args, **kwargs):
return func(*args, **kwargs)
print("end dec1")
return inner
def dec2(func):
print("start dec2")
def inner(*args, **kwargs):
return func(*args, **kwargs)
print("end dec2")
return inner
@dec2
@dec1
def func(a, b):
return a + b
print(func(1, 2))
不过外面需要注意一下生效的顺序:
start dec1
end dec1
start dec2
end dec2
3
可以看到时 dec1 装饰器先起作用,对于多个装饰器也是一样的。
带参装饰器
其实装饰器也是可以带参数的,我们可以定义一个更加复杂的装饰器:
def dec1(name):
def decorator(func):
def inner(*args, **kwargs):
if name == "zack":
print("hi zack")
elif name == "rudy":
print("hi rudy")
return func(*args, **kwargs)
return inner
return decorator
@dec1("zack")
def func(a, b):
return a+b
print(func(1, 2))
我们先来看下面的代码:
@dec1("zack")
def func(a, b):
return a +b
我们可以对 @dec1("zack") 进行如下理解,其中 dec1("zack") 的操作是执行 dec1 函数,并传入参数,而 dec1 的返回值是一个装饰器。这样我们就很好理解了。
因此,我们需要在 dec1 函数中定义一个装饰器。这可以从代码中看到。其它部分和之前没有区别,下面是输出结果:
hi zack
3
这种带参数的装饰器可以让我们的装饰器更加灵活,我们只需要通过不同参数就能让装饰器装饰不同的效果。