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_func2.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 world2.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
测试----hello2.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 mouse3.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 decorated4.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参考文献:
