(www.vn99.net) Python元类中的__call__和__new__的区别?
威尼斯人线上娱乐官网
威尼斯人线上娱乐官网
当前位置 : 威尼斯人线上娱乐官网 > www.vn99.net

(www.vn99.net) Python元类中的__call__和__new__的区别?

参考文档

creating-a-singleton-in-pythonwhat-is-a-metaclass-in-python

问题描述

下面这两段代码的执行结果反映了一个问题:很明显元类的存在会影响__call__和__new__的优先级,请问大神能否分析一下两者执行结果不同的原因?

实例代码

1.不含元类的单例模式

class Singletonobject:
    def __new__cls, *args, **kwargs:
        if not hasattrcls, _instance:
            cls._instance = superSingleton, cls.__new__cls, *args, **kwargs
        print__new__
        return cls._instance

    def __call__cls, *args, **kwargs:
        if not hasattrcls, _instance:
            cls._instance = superSingleton, cls.__call__*args, **kwargs
        print__call__
        return cls._instance

class FooSingleton:
    pass

print1
foo1 = Foo
print2
foo2 = Foo

printfoo1 is foo2  # True

上面这段代码的执行结果

$ python non_metaclass.py
1
__new__
2
__new__
True

2.含有元类的单例模式

class Singletontype:
    def __new__cls, *args, **kwargs:
        if not hasattrcls, _instance:
            cls._instance = superSingleton, cls.__new__cls, *args, **kwargs
        print__new__
        return cls._instance

    def __call__cls, *args, **kwargs:
        if not hasattrcls, _instance:
            cls._instance = superSingleton, cls.__call__*args, **kwargs
        print__call__
        return cls._instance


class Foometaclass=Singleton:
    # 不兼容Python2
    pass

print1
foo1 = Foo
print2
foo2 = Foo

print foo1 is foo2  # True

上面这段代码的执行结果

$ python metaclass.py
__new__
1
__call__
2
__call__
True

如果描述不够详细,请在评论区留一下言,我再改进。

我在这里再修改仔细说明下吧

元类是定义类结构属性的, 而类里的 "__new__", "__init__" 方法, 是处理类实例用的

我们所定义的每一个类, 都可以理解为是 type 的一个实例

class Fooobject:
    version = "1.0.1"
    pass

printFoo.version # 1.0.1
printFoo.__name__ # Foo

# ------------
# 使用type创建类是一样的
Foo = type"Foo", object,, dictversion="1.0.1"
printFoo.version      # 1.0.1
printFoo.__name__     # Foo

好, 说回到 "__new__" 和 "__call__"

元类中, "__new__" 会在你定义类的时候执行, 只执行一次, 如果有两个类使用了这个元类, OK, 会执行两次

class FooMetatype:
    def __new__meta, name, bases, dct:
        print"metaclass __new__"
        return typename, bases, dct

class Foo:
    __metaclass__ = FooMeta
    
# 没有任何代码了, 你会看到 print了metaclass __new__
# 如果你细心, 并联系上面说的, 理解为
# Foo = type"Foo", tuple, dict
# 就明白 FooMeta.__new__原来就是 type.__new__
# Foo 是type的一个实例
# 这是为什么定义元类都要base type的原因: class FooMetatype
# 如果你定义 __metaclass__ = type 并没什么错, 因为本来默认就是这样

而__call__会在你每次实例化的时候调用, 其实和Foo.__new__是一样的, 意思是, 如果你的Foo定义了__new__, 元类中的__call__便不会执行

class FooMetatype:
    def __new__meta, name, bases, dct:
        print"metaclass __new__"
        return typename, bases, dct

    def __call__self, *args, **kwargs:
        print"meta __call__"
        return "hello"


class Foo:
    __metaclass__ = FooMeta

    def __new__cls, *args, **kwargs:
        print"Foo __new__"
        return "hello"

f = Foo
printf
# 输出结果
# metaclass __new__
# Foo __new__
# hello

元类的 "__new__" 可以变更你所定义的类型, 我在这里定义Foo成了一个list

# -*- coding: utf-8 -*-
class FooMetatype:
    def __new__cls, name, bases, dct:
        return [1, 2, 3, 4]

# 这里相当于执行 Foo = [1, 2, 3, 4]
class Foo:
    __metaclass__ = FooMeta

printFoo          # [1, 2, 3, 4]
printFoo[0:2]     # [1, 2]
printFoo.pop    # 4

写这么多很辛苦的, 能正解理解metaclass的人非常的少, 不知题主要怎么感谢我

这不是元类影响new和call优先级的问题,而是元类在创建类的时候__new__只会被调用一次。而这个__new__就是用来创建出我们的类,如你问题中的。什么时候被创建呢?当解释器解释到Foo类的时候,会在类的定义中寻找__metaclass__属性,如果找到了就用它来创建类。如果没有找到,就会用内建的type来创建这个类。而后为什么不再调用呢,因为元类已经把我们的类创建好了,总不能每次Foo的时候重新去创建一次类吧。从你的输出中也可以看出foo2 = Foo并没有输出__new__。因为这个时候类已经被元类即Singleton创建好了,直接开始用就行了。和我们平时用的类没什么区别了,所以每一次创建的都是一个新的对象。而为什么要用__call__呢
因为Foo是元类Singleton创建出来的类,可以认为Foo是Singleton的实例对象。所以每次Foo的时候都会去调用__call__。而在__call__中我们拿到已经创建好的实例对象。不就是单例吗。。

再通过代码详细说明一下

class Singletontype:
    def __call__cls, *args, **kwargs:
        if not hasattrcls, _instance:
            printcls
            cls._instance = superSingleton, cls.__call__*args, **kwargs
            printcls._instance
        print__call__
        return cls._instance


class Foo:
    __metaclass__ = Singleton
    pass

print1
foo1 = Foo
print2
foo2 = Foo

print foo1 is foo2  # True

运行之后你会发现,__call__函数中cls是元类第一次也是唯一一次调用__new__创建出来的类即Foo, 而cls._instance则是我们的Foo的实例对象。每次Foo的时候都是取的同一实例对象。 元类毕竟是创建类的类。一旦创建好了,类就和平时定义的类没有什么两样。

栏目列表

广告位

威尼斯人线上娱乐官网