函数(方法)
或者类
的功能进行扩充,将扩充之后的函数(方法)
或者类
再返回。因此,在调用这个函数(方法)
或者类
的时候,就不会调用元素的函数(方法)
或者类
了,而是调用返回的新函数(新方法)
或者新类
一:
装饰器自身为函数
不带参数
新sleep函数
的时候,就是调用wrap函数,sleep函数的参数1,被wrap函数的*args、**kwargs这两个可变参数接收。整个装饰的目的就是将原生的sleep函数,扩充了一个print(nowTime() - start)
的过程。from time import sleep as sleeping
from time import time as nowTime
from functools import wraps
# 装饰器为函数
def timeit(fn):
@wraps(fn)
def wrap(*args,**kwargs):
start = nowTime()
ret = fn(*args,**kwargs)
print(nowTime() - start)
return ret
return wrap
# 装饰的对象为函数
@timeit
def sleep(n):
sleeping(n)
print("我睡了{}秒".format(n))
return n
# 调用装饰后的sleep函数
sleep(1)
print(sleep.__name__)
带参数
这个装饰器由于需要传递参数,因此需要两层装饰,第一层是接受传递的参宿,第二层是接收传递进来的需要装饰的函数
当sleep函数调用的时候,调用的并不是我们看到的原始的sleep函数,而是装饰过后的sleep函数。这个装饰的过程会发生在调用sleep函数之前发生。装饰的过程:装饰器第一次接收cpu_time参数,然后返回一个dec装饰器,而这个dec装饰器会被再次调用,传入参数是原生的sleep函数,原生的sleep函数作为参数传递到dec装饰器函数的fn参数,通过@wraps这个装饰器将fn的属性赋值给底下需要返回的wrap函数,最后返回wrap函数,由此可间,wrap就是装饰过后的sleep函数了。那么在调用新sleep函数
的时候,就是调用wrap函数,sleep函数的参数1,被wrap函数的*args、**kwargs这两个可变参数接收。整个装饰的目的就是将原生的sleep函数,扩充了一个time_func = time.clock if cpu_time else time.time
和print(nowTime() - start)
的过程。from functools import wraps
import time
# 装饰器自身为函数,且装饰器接受参数
def timeit(cpu_time=False):
time_func = time.clock if cpu_time else time.time
def dec(fn):
@wraps(fn)
def wrap(*args,**kwargs):
start = time_func()
ret = fn(*args,**kwargs)
print(time_func() - start)
return ret
return wrap
return dec
# 装饰对象为函数
@timeit(False)
def sleep(n):
time.sleep(n)
return n
# 调用装饰后的sleep函数
sleep(1)
print(sleep.__name__)
不带参数
from functools import wraps
def singleton(cls):
# 在装饰器中声明一个变量,用于保存类的实例,那么这个实例对象将始终是通一个实例对象
instance = None
@wraps(cls)
def wrap(*args,**kwargs):
# 使用nonlocal关键字将作用域扩展到上一级,拿到上一级的instance变量
nonlocal instance
# 如果instance非空,那么将instance通过cls类,也就是A进行初始化
if instance is None:
instance = cls(*args,**kwargs)
return instance
return wrap
# 装饰的对象是类
@singleton
class A:
def __init__(self):
self.name = 'yhy'
# 实例化装饰后的类
a = A()
print(id(a)) # 4321677152
b = A()
print(id(b)) # 4321677152
带参数
from functools import wraps
# 装饰器自身为函数
def singleton(instance=False):
name = 'yhy did it' if instance else 'yhy didn\'t it'
def dec(cls):
instance = None
@wraps(cls)
def wrap(*args,**kwargs):
nonlocal instance
print(name)
if instance is None:
instance = cls(*args,**kwargs)
return instance
return wrap
return dec
# 装饰的对象是类
@singleton(True)
class A:
def __init__(self):
self.name = 'yhy'
# 实例化装饰后的类
a = A()
print(id(a)) # 4321677152
b = A()
print(id(b)) # 4321677152
__set__
方法,取值的时候会调用__get__
方法。这个例子是一个经典的装饰器装饰类,使得被装饰的新类中的类变量绑定了描述器,因此,在给类变量赋值的时候,赋值过程是经过描述器赋值的,取值的时候也是通过描述器赋值的from functools import wraps
class TypeCheck:
def __init__(self, srcType, dstType):
self.srcType = srcType
self.dstType = dstType
# instance == a , cls == A
def __get__(self, instance, cls):
if instance is None:
return self
return instance.__dict__[self.srcType]
def __set__(self, instance, value):
if isinstance(value,self.dstType):
instance.__dict__[self.srcType] = value
else:
raise TypeError('{} should be {}'.format(value,self.dstType))
# 装饰器自身是一个函数
def typeassert(**kwargs):
def dec(cls):
def wraps(*args):
for name, required_type in kwargs.items():
setattr(cls, name, TypeCheck(name, required_type))
return cls(*args) # 这里是实例化新的Person类后返回实例对象,也就是p
return wraps
return dec
# 装饰对象是一个类,且带参数
@typeassert(name=str, age=int)
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 实例化新的Person类,这里相对于调用的是wraps函数
p = Person('yhy',18)
print(p.name)
print(p.age)
# 装饰器修改后的Person类为下面这个新的Person类,因此实例化Person的时候,调用的是下面这个新的Person类
class Person:
name = TypeCheck('name',str)
age = TypeCheck('age',int)
def __init__(self, name: str, age: int):
self.name = name
self.age = age
二:
装饰器自身为类
__get__
方法,此时装饰器扮演一个描述器的角色,当调用a.add()方法的时候,装饰器内部调用__get__
方法,返回一个装饰后的add方法。这个例子是一个@staticmethod的实现,也是一个经典的装饰器通过描述器的特性装饰函数的例子。虽然装饰器没有扩充函数什么功能,但是Python解释器不会给add()方法传递self参数了,这也算一个扩充吧,如果没有@staticmethod装饰add()方法,add方法定义的时候需要一个add(self)参数,来接受解释器传递的self实例,那add(self)就是一个实例方法,不是静态方法,因为Add调用这个add()方法的时候解释器不会传递self实例。那么使用装饰器装饰后的add方法,解释器将不会传递self,因此Add.add()调用是可以的from functools import wraps,partial
# 装饰器自身为类
class staticmethod:
def __init__(self, method):
wraps(method)(self)
def __get__(self, instance, cls):
# 通过partial函数返回一个带有None参数的add函数,因此在调用a.add()就不需要传递参数了
return partial(self.__wrapped__)
class Add:
def __init__(self, name, value):
self.name = name
self.value = value
# 这里装饰的是一个函数
@staticmethod
def add():
print('i am static method')
a = Add('yhy', 25)
a.add()
Add.add()
from functools import wraps, partial
class Classmethod:
def __init__(self,method):
wraps(method)(self)
def __get__(self, instance, cls):
# 通过partial函数返回一个带cls的method方法,
return partial(self.__wrapped__, cls)
class C:
@Classmethod
def method(cls,x='y'):
print(cls)
print(x)
# 调用装饰器返回的新method方法
C.method('yhy')
c = C()
c.method()
c.method('yhy')
输出为:
<class '__main__.C'>
yhy
<class '__main__.C'>
y
<class '__main__.C'>
yhy
__call__
函数,那么装饰器返回的就是这个__call__
函数,但是这个装饰器带有参数,因此,返回的是wrap函数。也就是说,装饰器装饰后的新函数为wrap函数,当reboot()调用的时候就是调用wrap函数,如果权限认证通过就返回fn()函数(这里的fn()是调用原生的reboot()函数)from functools import wraps
# 假定权限
def get_permissions():
return {'root'}
# 装饰器自身是类
class Require:
def __init__(self,permissions):
self.permissions = permissions
# 被装饰的类传递到fn
def __call__(self, fn):
@wraps(fn)
def wrap(*args, **kwargs):
if len(set(self.permissions).intersection(get_permissions())) <= 0:
raise Exception('Permissions denied')
return fn(*args, **kwargs)
return wrap
# 装饰器装饰的对象是函数
@Require({'root','admin'})
def reboot():
pass
__get__
方法,无法返回装饰后的类,但是有一个__call__
方法。因此A()就相当于调用了装饰器的__call__
方法。在__call__
方法里面self.__wrapped__
就是cls,也就是A类,实例化A类,创建一个实例self.instance,最后返回。因此,调用A().name,就是self.instance.namefrom functools import wraps
class Singleton:
# 这里的初始化函数只会调用一次,当第二次装饰的时候,这一步就滤过了
# 因此,第二次装饰的时候,不会再执行__init__方法,直接返回一个Singleton实例对象,
# 使得第二次调用__call__方法,发现self.instance有值,那么就直接返回了
def __init__(self,cls):
wraps(cls)(self)
self.instance = None
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.__wrapped__(*args,**kwargs)
return self.instance
# 装饰对象为类
@Singleton
class A:
def __init__(self):
self.name = 'yhy'
print(A().name)
# a和b是同一个实例
a = A()
b = A()