python装饰器@property

Posted 啸傲风月

tags:

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

装饰器示例

def w1(func):
    def inner():
        print(...验证权限...)
        func()

    return inner


@w1
def f1():
    print(f1 called)


@w1
def f2():
    print(f2 called)


f1()
f2()

输出结果:

...验证权限...
f1 called
...验证权限...
f2 called

当调用f1,f2函数时,首先执行了验证。通过一个闭包函数w1,调用函数上通过关键词@w1,对f1,f2完成了装饰。

当python解释器解释@w1时,会调用w1函数,同时将被修饰函数名传入(例如f1),在执行w1函数的时候,直接把inner函数返回了,同事把它赋值给f1,此时的f1已经不是未加修饰的f1了,而是指向了w1.inner函数地址。再调用f1函数,就好先执行权限验证,然后调用原来的f1(),该处的f1是通过参数传进来的f1.

执行时机

def w1(fun):
    print(...装饰器开始装饰...)

    def inner():
        print(...验证权限...)
        fun()

    return inner


@w1
def test():
    print(test)

test()

输出结果:

...装饰器开始装饰...
...验证权限...
test

由此可见,执行@w1时相当于执行了如下代码:

test = w1(test)

两个装饰器执行流程和修饰结果

def makeBold(fun):
    print(----a----)

    def inner():
        print(----1----)
        return <b> + fun() + </b>

    return inner


def makeItalic(fun):
    print(----b----)

    def inner():
        print(----2----)
        return <i> + fun() + </i>

    return inner


@makeBold
@makeItalic
def test():
    print(----c----)
    print(----3----)
    return hello python decorator


ret = test()
print(ret)

输出结果:

----b----
----a----
----1----
----2----
----c----
----3----
<b><i>hello python decorator</i></b>

可以发现,先用第二个装饰器(makeItalic)进行装饰,接着再用第一个装饰器(makeBold)进行装饰,而在调用过程中,先执行第一个装饰器(makeBold),接着再执行第二个装饰器(makeItalic)。

对有参数函数进行装饰

def w_say(fun):
    """
    如果原函数有参数,那闭包函数必须保持参数个数一致,并且将参数传递给原方法
    """

    def inner(name):
        """
        如果被装饰的函数有行参,那么闭包函数必须有参数
        :param name:
        :return:
        """
        print(say inner called)
        fun(name)

    return inner


@w_say
def hello(name):
    print(hello  + name)


hello(wangcai)

输出结果:

say inner called
hello wangcai

多个参数或不定个参数

def w_add(func):
    def inner(*args, **kwargs):
        print(add inner called)
        func(*args, **kwargs)

    return inner


@w_add
def add(a, b):
    print(%d + %d = %d % (a, b, a + b))


@w_add
def add2(a, b, c):
    print(%d + %d + %d = %d % (a, b, c, a + b + c))


add(2, 4)
add2(2, 4, 6)

输出结果:

add inner called
2 + 4 = 6
add inner called
2 + 4 + 6 = 12

对带返回值的函数进行修饰

def w_test(func):
    def inner():
        print(w_test inner called start)
        func()
        print(w_test inner called end)
    return inner


@w_test
def test():
    print(this is test fun)
    return hello


ret = test()
print(ret value is %s % ret)

输出结果:

w_test inner called start
this is test fun
w_test inner called end
ret value is None

可以发现,此时,并没有输出test函数的‘hello’,而是None,那是为什么呢,可以发现,在inner函数中对test进行了调用,但是没有接受不了返回值,也没有进行返回,那么默认就是None了,知道了原因,那么来修改一下代码:

def w_test(func):
    def inner():
        print(w_test inner called start)
        str = func()
        print(w_test inner called end)
        return str

    return inner


@w_test
def test():
    print(this is test fun)
    return hello


ret = test()
print(ret value is %s % ret)

输出结果:

w_test inner called start
this is test fun
w_test inner called end
ret value is hello

带参数的装饰器

def func_args(pre=xiaoqiang):
    def w_test_log(func):
        def inner():
            print(...记录日志...visitor is %s % pre)
            func()

        return inner

    return w_test_log


# 带有参数的装饰器能够起到在运行时,有不同的功能

# 先执行func_args(‘wangcai‘),返回w_test_log函数的引用
# @w_test_log
# 使用@w_test_log对test_log进行装饰
@func_args(wangcai)
def test_log():
    print(this is test log)


test_log()

输出结果:

...记录日志...visitor is wangcai
this is test log

通用装饰器

def w_test(func):
    def inner(*args, **kwargs):
        ret = func(*args, **kwargs)
        return ret

    return inner


@w_test
def test():
    print(test called)


@w_test
def test1():
    print(test1 called)
    return python


@w_test
def test2(a):
    print(test2 called and value is %d  % a)


test()
test1()
test2(9)

输出结果:

test called
test1 called
test2 called and value is 9 

类装饰器

装饰器函数其实是一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。
在python中,一般callable对象都是函数,但是也有例外。比如只要某个对象重写了call方法,那么这个对象就是callable的。

当创建一个对象后,直接去执行这个对象,那么是会抛出异常的,因为他不是callable,无法直接执行,但进行修改后,就可以直接执行调用了,如下

class Test(object):
    def __call__(self, *args, **kwargs):
        print(call called)


t = Test()
print(t())

下面,引入正题,看一下如何用类装饰函数。

class Test(object):
    def __init__(self, func):
        print(test init)
        print(func name is %s  % func.__name__)
        self.__func = func

    def __call__(self, *args, **kwargs):
        print(装饰器中的功能)
        self.__func()


@Test
def test():
    print(this is test func)


test()

输出结果:

test init
func name is test 
装饰器中的功能
this is test func

和之前的原理一样,当python解释器执行到到@Test时,会把当前test函数作为参数传入Test对象,调用init方法,同时将test函数指向创建的Test对象,那么在接下来执行test()的时候,其实就是直接对创建的对象进行调用,执行其call方法。

 


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

Python装饰器之 property()

python 装饰器和property

python属性装饰器[重复]

Python中的property类和@property装饰器

解决报错:在Python中使用property装饰器时,出现错误:TypeError: descriptor ‘setter‘ requires a ‘property‘ object but(代码片

python面向对象:组合封装property装饰器多态