( __init__ vs __new__ ) and __metaclass__

awesomeQA

  1. Why is init() always called after new()?
  2. What are metaclasses in Python?

python - Why is init() always called after new()?

我有点困惑为什么init总是在new之后被调用?

Here's an example:

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print "EXISTS"
            return A._dict['key']
        else:
            print "NEW"
            return super(A, cls).__new__(cls)

    def __init__(self):
        print "INIT"
        A._dict['key'] = self
        print ""

a1 = A()
a2 = A()
a3 = A()
Outputs:

NEW
INIT

EXISTS
INIT

EXISTS
INIT
Why?

在面向对象语言中,会有类似SomeClass(arg1, arg2)的表达式会用于创建、初始化、返回类的实例,在这其中呢, “初始化实例属性”的部分会被每个类构造器实现,这也是对类实例的一部分操作代码,用来设置类的初始条件。而在python里,这是__init__方法,同时__new__ 用来创建新的实例(malloc a new instance part)。可以对new其进行修改,例如写个单例,所以在python里__new__不一定涉及分配;我们对__new__所期望的就是他应该产生一个恰当的实例。

class S(object):
    c = None
    def __new__(cls):
        print(cls, 'new...')
        if not cls.c:
            cls.c = object.__new__(cls)  # 只负责类的创建 
        return cls.c

    def __init__(self):
        print(self, 'init...')
        
>ss0 =  S1()
(, 'new...')
(<__main__.S1 object at 0x7f1f82c86790>, 'init...')
>ss1 =  S1()
(, 'new...')
(<__main__.S1 object at 0x7f1f82c86790>, 'init...')

当然还有其他的创建单例的方法,例如使用meta class。Metaclass(元类)是类的类(用于创建类对象的东西,具体下面会有解释。),并且class’__call__方法控制着当你调用类的实例时的行为。所以一个metaclass’__call__方法控制着当你调用类的时候的行为(实例化)。i.e. 这也就允许你 重新定义类实例的创建机制。在这一层次可以非常优雅的实现非标准实例创建过程的,例如单例模式。事实上,小于10行代码就可以实现Singleton metaclass,且可以将任何正常的类转换为单例类,通过增加 __metaclass__ = Singleton 。

class Singleton(type):    
    __instance = None

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance

至于为什么修改metaclass 的 __call__方法能够干预类的实例化,请看以下过程

class Foo(meta_class=Singleton):
     pass

foo = Foo()

在执行Foo()时,其实调用了Foo.call(), 而在调用__call__时,其实际执行的是metaclass的__call__方法,而__call__方法是用于创建实例的,从而干预了Foo实例的创建。__new____init__是python对外暴露的用于干预__call__执行内容的方法。(另外一个原因是,在metaclass中 修改__call__方法,不进行super().call调用,会导致__init____new__方法都不执行。)

What are metaclasses in Python?

类作为对象

在理解metaclass之前呢,咱需要掌握python中的class。在大多数语言中,类只是用来描述如何产生对象的那一部分代码。这在python的世界里,从某方面来说是正确滴。

> class ObjectCreator(object):
        pass
>my_obj = ObjectCreator()
>print(my_obj)
<__main__.ObjectCreator object at 0x8974f2c> # 这里也可以注意一下。单独创建的类的module名字是`__main__`。这也是单独运行脚本时经常会产生包import error的原因。

但是在python里,classes也是objects。当你使用class 关键字的时候,python执行并且创建一个对象。这个结构

> class ObjectCreator(object):
        pass

在内存中创建了名为 ObjectCreator的对象。这个对象(the class)的功能是创建对象(the instances)。但是总而言之类还是对象。所以

  • 可以把类赋给变量
  • 可以拷贝类
  • 可以给类加参数
  • 可以将类传递作为方法的参数去使用

如何动态的创建类呢

既然类是对象的话,那么就可以动态的创建类,就像创建对象一样。
首先你可以用class关键字创建。但这个不是动态的。因为你得自己写整个类的结构。
那当你使用class关键字的时候,python自动的创建了对象的话,那python是怎么创建类的呢?搞明白了这个,那我们就可以自己创建了。

记得type函数吗?让你可以知道对象的类型的那个。type也可以有完全不同的能力,比如说动态的创建类什么的。type可以接受类的描述作为参数,然后返回一个类。
(我知道一个方法根据参数不同有两种完全不同的功能有点傻傻的,但是这是python为了向后兼容产生的问题)
Type是这样使用的

type(name of the class,
       tuple of the parent class (for inheritance, can be empty),
        dictionary containing attributes names and values
)

e.g.:

> class MyshinyClass(object):
        pass
# can also created manually this way
> MyShinyClass = type('MyShinyClass', (), {})
> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>
# type accepts a dictionary to define the attributes of the class. So:
> class Foo(object):
...       bar = True
> Foo = type('Foo', (), {'bar':True})
# Eventually you'll want to add methods to your class. Just define a function with the proper signature and assign it as an attribute.
> def echo_bar(self):
...       print(self.bar)
...
> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
> hasattr(Foo, 'echo_bar')
False
> hasattr(FooChild, 'echo_bar')
True

从此就可以动态的创建类啦。上述就是当你使用class关键字的时候,python所做的,而这些就是使用metaclass实现的

啥是metaclass(finally)

Metaclass是哪些用来创建类的东西。我们定义类来创建对象,但是类也是对象,而metaclass就是python用来创建类这种对象的东西。你可以这样理解

MyClass = MetaClass()
my_obj = MyClass()

可知,type让你可以这样做

Myclass = type('Myclass', (), {})

这是因为type实际上是metaclass。type是python用来在幕后创建类的metaclass。那么为什么type首字母小写呢,为啥不是Type?我猜是因为和str的一致性保持,用来创建string对象的类。可以用__class__属性确认。

所有东西,在python里都是对象。包括int,string,function,和class。所有这些对象都是从class创建的。

>>> age = 35
>>> age.__class__

>>> name = 'bob'
>>> name.__class__

>>> def foo(): pass
>>> foo.__class__

>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__

__class____class__呢?

>>> age.__class__.__class__

>>> name.__class__.__class__

>>> foo.__class__.__class__

>>> b.__class__.__class__

所以,mataclass就是创建类对象的东西。
你也可以自己自定义一个类的工厂呀。定义一个自己metaclass

metaclass 属性

在python2里,可以__metaclass__属性可以这样使用

class Foo(object):
        __metaclass__ = somethins...
        [...]

如果这样做呢,python会用指定的__metaclass__去创建类Foo。
这儿有点trick,
你需要提前写class Foo(object),但是类对象Foo并可以在内存里创建。python会首先在类定义里查找__metaclass__,如果找到了,会用它创建类对象Foo。如果没有找到会用type创建。

当你在

class Foo(Bar):
        pass

Python做了以下事情。
__metaclass__是Foo的属性吗?
如果是,用__metaclass__对应的东西创建Foo这个类对象。
如果没有,python会在module层寻找__metaclass__属性。下一步操作如上
如果没有找到任何的__metaclass__那会使用Bar(继承的第一个类)的__metaclass__来创建类对象
NOTION: __metaclass__不能被继承。e.g. Bar使用了__metaclass__属性入创建类对象,作为子类Foo不会继承这个属性。

所以现在的问题是,metaclass里是什么呢?
回答是: 用来创建类的东西。
什么可以创建类呢?
type或者继承type的子类

在python3中的__metaclass__
创建metaclass的语法在python3中改为

class Foo(object, metaclass=something)
 ...

Metaclass作为参数传递进去,功能大同小异

自定义metaclass

通过继承type实现。

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type.__new__(cls, clsname, bases, uppercase_attr)

或者用super实现,这样对于继承来说会更加清晰

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

实际上,元类特别适用于制作黑魔法,因此也很复杂。但就其本身而言,它们很简单:
拦截一个类创建
修改类
返回修改后的类

Why would you use metaclasses?

Metaclasses are deeper magic that 99% of users should never worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).

Python Guru Tim Peters

总结

类也是对象
元类是用来创建类的类
一切都是Python中的一个对象,它们都是类的实例或元类的实例。除了类型。type实际上是它自己的元类。这不是你可以在纯Python中重现的东西。其次,元类很复杂。可能不希望将它们用于非常简单的类更改。可以使用两种不同的技术更改类:猴子补丁和类装饰器99%的时间你需要改变类,你最好使用这些。但是98%的情况下,你根本不需要改变类。

参考 :

  1. python - Why is init() always called after new()? - Stack Overflow
  2. metaclass!超赞!
  3. 简述 initnewcall 方法 - FooFish-Python之禅

最后:实践出真知

你可能感兴趣的:(( __init__ vs __new__ ) and __metaclass__)