小门板儿

Menu

python3-装饰器

一、函数

1.1、在函数中定义函数

def hi(name="python"):
    print("now you are inside the hi() function")
​
    def greet():
        return "now you are in the greet() function"
​
    def welcome():
        return "now you are in the welcome() function"
​
    print(greet())
    print(welcome())
    print("now you are back in the hi() function")
​
hi()
输出:
D:\Programs\Python\Python37\pythonw.exe D:/pytest/learn01/d/deco.py
now you are inside the hi() function
now you are in the greet() function
now you are in the welcome() function
now you are back in the hi() function

上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用,然后greet()和welcome()函数在hi()函数之外是不能访问的

1.2、从函数中返回函数

其实并不需要在一个函数里去执行另一个函数,我们也可以将其作为输出返回出来

def hi(name="python"):
    print("now you are inside the hi() function")
​
    def greet():
        return "now you are in the greet() function"
​
    def welcome():
        return "now you are in the welcome() function"
​
    if name=='python':
        return greet
    else:
        return welcome
​
a=hi()
print(a)
print('---------')
print(a())
输出:
now you are inside the hi() function
<function hi.<locals>.greet at 0x00000000021C4620>
---------
now you are in the greet() function

在 if/else 语句中我们返回 greet 和 welcome,而不是 greet() 和 welcome()。为什么那样?这是因为当你把一对小括号放在后面,这个函数就会执行;然而如果你不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。上面可以看到, a = hi(),hi() 会被执行,而由于 name 参数默认是 python,所以函数 greet 被返回了。

1.3、将函数作为参数传给另一个函数

def hi():
    return "hi python"
​
def doSomethingBeforeHi(func):
    print("I am doing some boring work before executing hi()")
    print(func())
​
doSomethingBeforeHi(hi)
输出:
I am doing some boring work before executing hi()
hi python

二、装饰器

一个简单的例子

def a_new_deco(func):
    def WrapTheFuction():
        print("I am doing some boring work before executing func()")
        func()
        print("I am doing some boring work after executing a_func()")
    return  WrapTheFuction
​
​
def a_func():
    print("i am the function")
​
func=a_new_deco(a_func)
func()
输出:
I am doing some boring work before executing func()
i am the function
I am doing some boring work after executing a_func()

2.1、被装饰的函数不带参数

python 中装饰器做的事情就是封装一个函数,并且用这样或者那样的方式来修改它的行为。现在使用@符号来简化一下

def a_new_deco(func):
    def WrapTheFuction():
        print("I am doing some boring work before executing func()")
        func()
        print("I am doing some boring work after executing a_func()")
    return  WrapTheFuction
​
@a_new_deco
def a_func():
    print("i am the function")
​
a_func()
输出:
I am doing some boring work before executing func()
i am the function
I am doing some boring work after executing a_func()

但是我们运行如下代码会存在一个问题

print(a_func.__name__)
输出:
WrapTheFuction

这里的函数被warpTheFunction替代了。它重写了我们函数的名字和注释文档(docstring)。幸运的是Python提供给我们一个简单的函数来解决这个问题,那就是functools.wraps。我们修改上一个例子来使用functools.wraps:

from functools import wraps
​
def a_new_deco(func):
    @wraps(func)
    def WrapTheFuction():
        print("I am doing some boring work before executing func()")
        func()
        print("I am doing some boring work after executing a_func()")
    return  WrapTheFuction
​
@a_new_deco
def a_func():
    print("i am the function")
​
a_func()
print(a_func.__name__)
输出:
I am doing some boring work before executing func()
i am the function
I am doing some boring work after executing a_func()
a_func

2.2、被装饰的函数带参数

from functools import wraps
​
​
def my_decorator(func):
    @wraps(func)
    def inner(*args,**kwargs):
        print("I am doing some boring work before executing func()")
        func(*args, **kwargs)
        print("I am doing some boring work after executing a_func()")
    return inner
​
@my_decorator
def test_func(args):
    print('test:%s'%args)
​
test_func('hello world')
输出:
I am doing some boring work before executing func()
test:hello world
I am doing some boring work after executing a_func()

2.3、装饰器带参数

def my_decorator(name):
    def outer(func):
        @wraps(func)
        def inner(*args, **kwargs):
            print("********")
            print("添加带装饰器参数%s的功能代码" % name)
            func(*args, **kwargs)
        return inner
    return outer
​
@my_decorator(name='settings')
def test_func(arg):
    print("测试----%s" % arg)
test_func("hello world")
输出:
********
添加带装饰器参数settings的功能代码
测试----hello world

2.4 基于类封装的不带参数装饰器

call ()方法是将实例成为一个可调用对象(即callable对象),同时不影响实例的构造,但可以改变实例的内部值。

class MyDecoretor:
    def __init__(self,func):
        self.func=func
​
    def __call__(self, *args, **kwargs):
        print("********")
        print("输出当前时间",time.time())
        return self.func(*args, **kwargs)
​
@MyDecoretor
def test_func(args):
    print("测试----%s" % args)
​
test_func('hello')
输出:
********
输出当前时间 1623829668.3424222
测试----hello

2.5 基于类封装的带参数装饰器

class MyDecorator:
    def __init__(self,name):
        self.name=name
​
    def __call__(self, func):
        def inner(*args,**kwargs):
            print("********")
            print("添加带装饰器参数%s的功能代码" % self.name)
            func(*args, **kwargs)
        return inner
​
@MyDecorator(name="setting")
def test_func(arg):
    print("测试----%s" % arg)
​
test_func('hello')
输出:
********
添加带装饰器参数setting的功能代码
测试----hello

三、常用的内置装饰器

3.1、@property装饰器

1、@property:将一个方法变为属性调用。未添加装饰器@property时,函数类型是一个方法:<class 'method'>;添加装饰器@property时,函数类型是返回值的类型:如,<class 'str'> 2、property对象的setter方法:表示给属性添加设置功能,即可修改属性值。 若未添加设置属性,就设置新值,则会引发错误AttributeError: can't set attribute。 3、property对象的deleter方法:表示给属性添加删除功能 若未添加删除属性,就删除属性则会引发错误AttributeError: can't delete attribute。

class Deco1:
    def __init__(self,name):
        self.__name=name
​
    @property
    def get_name(self):
        return self.__name
​
    @get_name.setter
    def get_name(self,value):
        self.__name=value
​
    @get_name.deleter
    def get_name(self):
        del self.__name
​
test1=Deco1('hello')
print(test1.get_name)
test1.get_name='happy'
print(test1.get_name)
# 删除get_name属性:删除属性需使用装饰器@property的deleter函数;
del test1.get_name
print(test1.get_name)
输出:
hello
happy
Traceback (most recent call last):
  File "D:/pytest/learn01/d/deco_8.py", line 22, in <module>
    print(test1.get_name)
  File "D:/pytest/learn01/d/deco_8.py", line 7, in get_name
    return self.__name
AttributeError: 'Deco1' object has no attribute '_Deco1__name'

3.2、类对象中的方法

类对象中的方法:实例方法、类方法和静态方法

实例方法:函数中的第一个参数为self的方法 静态方法:使用@staticmethod装饰器来将类中的函数定义为静态方法。类中创建的一些方法,但该方法并不需要引用类或实例。静态方法通过类直接调用,无需创建对象,也无需传递self。 类方法:使用@classmethod装饰器来装饰类中的函数定义为类方法。类方法不需要实例化,也不需要self参数,函数中第一个参数是自身的cls参数,可用来调用类的属性、方法和实例化对象。

3.2.1、staticmethod装饰器

staticmethod 就是将该被装饰的函数与该类没有关系,该函数不能用self传参,需要和普通函数一样传参

作用: 1、限制名称空间,这个函数虽然是个普通的函数,但只有这个类里能用到 2、代码易读性.静态方法并不需要self参数 3、节约内存.不用每个实例都实例化方法. 4、在类内部控制静态方法.不然的话换一种实现方法,把函数放在在类外部

class  Cat():
    def __init__(self,name):
        self.name=name
​
    @staticmethod
    def play(name,act):
        print("%s is playing %s"%(name,act))
​
c=Cat('hahahh')
#通过对象调用方法
c.play("dddd","ball")
#通过类调用方法
Cat.play('eeeee',"mouse")
输出:
dddd is playing ball
eeeee is playing mouse

3.2.2、classmethod装饰器

class A():
    name='hahahh'
    def func_a(self):
        print('func_a')
​
    @classmethod
    def func_b(cls):
        print('func_b')
        print(cls.name)
        cls().func_a()# 调用func_a 方法
​
# 不需要实例化
A.func_b()
输出:
func_b
hahahh
func_a

四、使用场景

4.1、授权(Authorization)

装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权:

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            authenticate()
        return f(*args, **kwargs)
​
    return decorated

4.2、日志(Logging)

from functools import wraps
​
​
def logit(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
​
    return with_logging
​
@logit
def addition_func(x):
    """Do some math."""
    return x + x
​
result = addition_func(4)
​
输出:
addition_func was called

参考文献:

https://www.runoob.com/w3cnote/python-func-decorators.html

https://www.jianshu.com/p/d48ee5da15f2

— 于 共写了6894个字
— 标签:

评论已关闭。