昨天收官之作,以为自己懂的差不多了,但今天看了工作中的框架源码。
def with_metaclass(meta, *bases): """Create a base class with a metaclass.""" # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. class metaclass(meta): def __new__(cls, name, this_bases, d): return meta(name, bases, d) return type.__new__(metaclass, str('temporary_class'), (), {})
直接一脸懵逼了。
参考链接:https://liqiang.io/post/understanding-python-class-instantiation
从头开始梳理一遍,先写轻松一点的从类到对象,前一篇已经讲过,类实例化的过程是在调用元类的__call__属性,参考链接找到
type中的__call__的逻辑如下
class Mymeta(type): def __call__(self, *args, **kwargs): obj = self.__new__(self, *args, **kwargs) # 返回对象是类的实例才执行__init__ if isinstance(obj, self): # self.__init__(obj, *args, **kwargs) obj.__init__(*args, **kwargs) return obj
这个应该是Mymeta的__call__的逻辑,首先执行类的__new__方法,创建一个空对象,默认使用的object.__new__方法,而且只接受一个类对象。然后通过判断生成对象是否为类的实例判断是否执行__init__。
所以当你从__new__随便返回任何数据都可以,只有是否执行__init__的区别。
今天晚上看来剩下的时间还是留给__new__。上面的函数卡住我将近一天,主要还是我对类的继承理解有问题,对__new__的理解也不够测底。
个人的理解:一个类对象初始状态是没有任何属性的。就因为没有任何的属性,所以任何一个类都继承与object,由object给予子类与属性。
这里我个人可以分为两条线,一条线是比较容易理解的普通类,普通类默认的状态下,所有的类都继承与obejct。所以很明显,__new__的属性也继承来至object,这个一个没有__get__属性的可调用对象[也被称为静态方法]。
所以不管是类还是实例,调用该object.__new__都需要传入一个被实例的类,返回值是属于该类的实例。在普通的情况下,实例化一个对象的逻辑也是通过调用元类的__calll__属性,然后通过object.__new__来首先创建一个无私有属性的对象。所以当一个类继承了一个修改过__new__属性的类的时候,如果自身没有重写__new__属性,他就会执行Python的继承原则,优先调用父类的__new__属性。
所以当你通过修改__new__内部逻辑实现单例的类,所有继承与它的类一般情况下都是单例。
还有一条线就是元类,所有的类的创建都是基础元类中的__new__方法实现,如有特殊需求,可以在创建的元类中自定义返回的初始类。
如果理解了这个,那下面这个函数就可以理解了。
def with_metaclass(meta, *bases): """Create a base class with a metaclass.""" # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. class metaclass(meta): def __new__(cls, name, this_bases, d): return meta(name, bases, d) return type.__new__(metaclass, str('temporary_class'), (), {})
首先前提已经提过了,通过type可以直接创建一个类,通过type.__new__也可以创建一个类,多了第一个参数的元类选项,创建一个属于这个元类的类。
我没由去寻找type的源码,很明显type的__new__执行跟object.__new__逻辑应该一样,但实现方式有所不同。元类的__new__需要创建一个类,一个初始化的类。
前面那篇博文我已经写名了,我们无法再去定义类->元类->上一级造物主对象的__call__,因为type还是type创建的,所以根据常规的类的初始化的逻辑,先执行__new__创建一个新对象,然后再执行__init__初始化对象的属性。
type.__new__前面已经介绍,功能比较强大,默认情况下传入第一个参数为创建类的元类。在一个没有修改过__new__参数的元类。
Mymeta('Myclass', (), {}) type.__new__(Mymeta, 'Myclass', (), {})
Mymeta是一个没有修改__new__属性的元类,这两个效果是一样的
class Mymeta(type): def run(cls): print('run') name = 'sidian' D1 = Mymeta('D1', (),{}) D2 = type.__new__(Mymeta, 'D1', (),{}) print(dir(D1)) print(dir(D2)) D1.run() print(D2.name)
运行的这段代码的输出,元类的属性虽然被创建的类可以调用,但在dir的时候都不能显式该属性,只能通过hasattr来判断。元类由太多的骚操作了。
时间迟了,对于框架中的这个函数。首先它返回的是一个类,这个要确认,这个方法很的使用场景也很特别,一般写在这种地方。
class Strategy(with_metaclass(MetaStrategy, StrategyBase)): ''' Base class to be subclassed for user defined strategies. '''
在这里定义,可以让类继承这个函数创建的类,但这个函数创建的类是一个傀儡,这个傀儡类由一个傀儡元类创建,内部定义的__new__返回的才是真正需要的类。
class metaclass(meta): def __new__(cls, name, this_bases, d): return meta(name, bases, d) return type.__new__(metaclass, str('temporary_class'), (), {})
默认情况下,当一个类被继承创建一个类的情况是在__new__中的默认操作为type('子类的类名', (父类,),{子类的命名空间})
但很明显这个函数的作用,是指定了父类,还有指定了元类的作用,而且还可以操作自己的属性空间。这个真的是相当骚的写法。
就这个写法
class Strategy(with_metaclass(MetaStrategy, StrategyBase)):
如果转成为显式的写法
class Strategy(StrategyBase, metaclass = MetaStrategy):
实际测试效果是一样的,一个明明可以很简单就可以看懂的继承,框架用了这么复杂的函数,实在让人震惊,也可能当时局限在py2的情况下,才不得已用了该途径的操作。
但也让我对__new__这个对象有了更加深入的理解。
最后我也总算知道了,为什么元类都喜欢在元类的定义上面加一个Meta,主要是为了便于从名称标记哪个是元类,那个是普通类。因为说到底,这两个都是类,一定元类定义完成,从print输出,你更本无法判断那个是元类哪个是类。
class Mymeta(type): def run(cls): print('run') name = 'sidian' class Demo: ... print(Mymeta) print(Demo) print(Mymeta.__class__) print(Demo.__class__)
输出
<class '__main__.Mymeta'> <class '__main__.Demo'> <class 'type'> <class 'type'>
今天就先写到这里,在工作中用到的那个框架里面,用到了大量的元类操作,元类中定义了大量的方法,修改了__call__的实现,后期发现经常的继续补上。