12.类装饰器是什么(用于装饰类的装饰器)
注意与装饰器类的区别
- 装饰器的原理就是将函数对象传入,功能增强后再传出,因此函数可以被装饰器装饰;
- 同理,类也可以作为对象被传入,也可以作为对象被传出,所以类也可以被装饰器装饰。
13.一个类实例的创建过程——__new__
__new__()
是一种负责创建类实例的静态方法,它无需使用 @staticmethod
装饰器修饰,且该方法会优先 __init__()
初始化方法被调用。
一般情况下,覆写 __new__()
的实现将会使用合适的参数调用其超类的 super().__new__()
,并在返回之前修改实例。例如:
13_newMethodInCls.py
class DemoClass:
instances_created = 0
def __new__(cls, *args, **kwargs):
print("__new__():", cls, args, kwargs)
instance = super().__new__(cls)
print('instance:', instance)
instance.number = cls.instances_created
cls.instances_created += 1
return instance
def __init__(self, attribute):
print("__init__():", self, attribute)
self.attribute = attribute
test1 = DemoClass("abc")
print('test1:', test1)
print("\n----------------\n")
test2 = DemoClass("xyz")
print('test2:', test2)
print("\n----------------\n")
print(test1.number, test1.instances_created)
print(test2.number, test2.instances_created)
输出结果为:
__new__(): <class '__main__.DemoClass'> ('abc',) {}
instance: <__main__.DemoClass object at 0x00000161CA9F2EB0> 0
__init__(): <__main__.DemoClass object at 0x00000161CA9F2EB0> abc
test1: <__main__.DemoClass object at 0x00000161CA9F2EB0>
----------------
__new__(): <class '__main__.DemoClass'> ('xyz',) {}
instance: <__main__.DemoClass object at 0x00000161CA9F2DF0> 1
__init__(): <__main__.DemoClass object at 0x00000161CA9F2DF0> xyz
test2: <__main__.DemoClass object at 0x00000161CA9F2DF0>
----------------
0 2
1 2
Python中存在于类中的构造方法
__init__()
负责将类实例化,而在__init__()
执行之前,__new__()
负责制造这样的一个实例对象,以便__init__()
去让该实例对象更加的丰富(为其添加属性等)。同时:
__new__()
方法还决定是否要使用该__init__()
方法,因为__new__()
可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例。
如果将类比喻为工厂,那么init()方法则是该工厂的生产工人,init()方法接受的初始化参 数则是生产所需原料,init()方法会按照方法中的语句负责将原料加工成实例以供工厂出货。而 new()则是生产部经理,new()方法可以决定是否将原料提供给该生产部工人,同时它还决定着出 货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品。
需要注意的是
- 在实例化
test1
的时候,“项目经理”把自己的cls.instances_created
和test1.instances_created
地址弄成一样的了 - 在在实例化
test2
的时候,“项目经理”还是那个项目经理,把自己的cls.instances_created
地址改变赋给test2.instances_created
,此时test1.instances_created
地址跟着改变。
所以最后test1
、test2
的instances_created
属性值一样。
用这个原理,就可以将__new__
重新覆写,让他返回单例
class Singleton(object):
def __new__(cls):
# 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton, cls).__new__(cls)
return cls.instance
obj1 = Singleton()
obj2 = Singleton()
obj1.attr1 = 'value1'
print(obj1.attr1, obj2.attr1)
print(obj1 is obj2)
输出结果为:
value1 value1
True
14.构造类装饰器——用函数装饰器
假设有很多类,但是都需要加上一个输出偶数的方法,以14_decoratorForClsExample.py
其中的一个NumProcessing
类为例
def print_evens(self):
for i in range(self.num):
if i % 2 == 0:
print(i)
def decorator(cls):
print("开始对类进行装饰")
cls.print_evens = print_evens
return cls
@decorator
class NumProcessing:
num = 1000
def __init__(self, num):
print("开始初始化")
self.num = num
def print_odds(self):
for i in range(self.num):
if i % 2 == 1:
print(i)
if __name__ == '__main__':
num_processing = NumProcessing(5)
num_processing.print_odds()
num_processing.print_evens()
输出结果为;
开始对类进行装饰
开始初始化
1
3
0
2
4
这个装饰器的逻辑就是
- 将
NumProcessing
这个类对象传入decorator
装饰器中 - 然后在装饰器中新增成员
print_evens
- 再将输出偶数的函数对象
print_odds
传入新增成员print_evens
,使其成为方法 - 最后将装饰好的类返回
总结:
用大白话说,@装饰器
这个语法糖就是把被装饰的函数对象作为参数传进@
后面跟着的东西再加个括号,比如如果是@A
,那么被装饰的函数其实就是被包裹进A()
里面,如果@A()
,那么就是被包裹进A()()
里面因为A()
返回的才是起装饰作用的函数,A()
传进去的是装饰器的参数,这也是为什么装饰函数或者装饰类需要能调用的原因。