闭包与装饰器

Posted w-wang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了闭包与装饰器相关的知识,希望对你有一定的参考价值。

闭包

主要为装饰器服务,解决函数内变量的作用域问题。

闭包有两个函数构成,分为外部函数和内部函数,内部函数玩长城代码逻辑,外部函数范围内部函数的引用以及所携带的信息(内存占用,变量等),所携带的信息在外部函数执行结束之后消失,而会继续待在所返回的函数引用里面。如果想要删掉该函数所携带的信息,可以使用del删掉该引用所留存的信息。

def outer_func():
    a = [1, 2, 3, 4]

    def inner_func():
        a[0] += 1
        print(a)
    return inner_func

var = outer_func()
type(var)
>>> function outer_func.<locals>.inner_func at 0x0000029C3E0C4438>
# var() == inner_func()
var()
>>> [2, 2, 3, 4]
var()
>>> [3, 2 ,3 ,4]

del var
# delete所携带的信息
var = outer_func()
var()
>>> [2, 2, 3, 4]

通过这一特性,我们可以实现函数的装饰。

装饰器

函数装饰器

装饰器,顾名思义,就是用来给函数增加某些功能的,一方面可能是需要装饰的函数太多,所以需要装饰函数的功能,另一方面是函数不允许修改源代码,只能封装后添加功能。

def func1(func):
    def wrapper():
        print(‘添加内容‘)
        func()
    return wrapper

@func1
def for_print(word_press):
    print(‘输出内容‘)

for_print(‘hello world‘)

当执行for_print()[11]时,该段程序首先执行func1()[1],将for_print作为函数引用传递到func1,然后执行wrapper函数,再执行传入的函数引用,然后返回wrapper,此时wrapper表示函数的for_print的引用,即wrapper == for_print

如果不用语法糖的话,可以这样写:

def func1(func):
    def wrapper(wd):
        print(‘添加内容‘)
        func(wd)
    return wrapper

def for_print(word_press):
    print(‘输出内容:{}‘.format(word_press))

<-!-在这里修改->    
for_print = func1(for_print)
# 此时func1(for_print)返回的是wrapper,赋值给for_print
for_print(‘hello world‘)

# 等价于
func1(for_print)(‘hello world‘)

上面我们在介绍不用语法糖的时候已经介绍了被修饰函数含有参数的情况,还有一种情况:被修饰函数有返回值,即return:

def func1(func):
    def wrapper(wd):
        print(‘添加内容‘)
        a = func(wd)
        return a  <-!-在这里修改->
    return wrapper

@func1
def for_print(word_press):
    a = 1
    print(‘输出内容:{}‘.format(word_press))
    return a

a = for_print(‘hello world‘)

这里可以拆解成几步:

  • a = for_print(‘hello world‘) --> a = func1(for_print)(‘hello word‘)
    • for_print = func1(for_print) --> wrapper
    • for_print(‘hello world‘) --> wrapper(‘hello world‘)
    • a = return a --> wrapper函数的返回值
类装饰器

看完了函数的装饰器,我们可以再补充类的装饰器,这个也很有意思:

class Foo(object):
    def __init__(self, to_decorate):   <--!注意这里,类装饰器在这里写入引用函数-->
        self.to_decorate = to_decorate    

    def __call__(self, var):
        print(‘class decorator runing‘)
        self.to_decorate(var)
        print(‘class decorator ending‘)

@Foo
def to_decorate(var):
    print(var)
    print(‘bar‘)

to_decorate(3)

使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

attributeproperty

类里面还有一种比较方面的用法,主要用来区分attributeproperty

class Zoo(object):
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if ~(0 < age < 18):
            self.__age = age
        else:
            print(‘非法使用未成年动物‘)


animal = Zoo(‘Monkey‘, 20)
print(animal.age)
animal.age = -2000
print(animal.age)

如果没有下面这段代码,

@age.setter
def age(self, age):
    if ~(0 < age < 18):
        self.__age = age
    else:
        print(‘非法使用未成年动物‘)

那么,animal.age = -2000,即对年龄属性进行赋值,是不允许的。

而使用animal.age = -2000这行代码的前提是:

@property
def age(self):
    return self.__age

需要对这个属性先进行装饰,否则@age.setter没有办法去装饰。

这样做的好处是,即便我们不写animal.ageGetter(self)animal.ageSetter(self, age)函数就可以对该属性进行取值和修改,而且比函数更加方便。

注意:以上是使用属性名作为函数名,并反对此函数进行复写,然后根据需求去调用装饰的函数,不能更改且顺序不能更换,@property要装饰def age(self):,如agedef age(self):def age(self, age):

以上是关于闭包与装饰器的主要内容,如果未能解决你的问题,请参考以下文章

python11 装饰器与闭包

闭包与装饰器

python 闭包与装饰器

闭包函数与装饰器

Python闭包与装饰器

python3命名空间与作用域,闭包函数,装饰器