设计模式 - Python元类中的__call__和__new__的区别?
大家讲道理
大家讲道理 2017-04-18 10:04:49
0
2
526

参考文档

creating-a-singleton-in-python和what-is-a-metaclass-in-python

问题描述

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

实例代码

1.不含元类的单例模式

class Singleton(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) print('__new__') return cls._instance def __call__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): cls._instance = super(Singleton, cls).__call__(*args, **kwargs) print('__call__') return cls._instance class Foo(Singleton): pass print('1') foo1 = Foo() print('2') foo2 = Foo() print(foo1 is foo2) # True

上面这段代码的执行结果

$ python non_metaclass.py 1 __new__ 2 __new__ True

2.含有元类的单例模式

class Singleton(type): def __new__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) print('__new__') return cls._instance def __call__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): cls._instance = super(Singleton, cls).__call__(*args, **kwargs) print('__call__') return cls._instance class Foo(metaclass=Singleton): # 不兼容Python2 pass print('1') foo1 = Foo() print('2') foo2 = Foo() print (foo1 is foo2) # True

上面这段代码的执行结果

$ python metaclass.py __new__ 1 __call__ 2 __call__ True

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

大家讲道理
大家讲道理

光阴似箭催人老,日月如移越少年。

reply all (2)
黄舟

I will modify it and explain it carefully here

The metaclass defines the class structure attributes, and the "__new__" and "__init__" methods in the class are used to process class instances

Every class we define can be understood as an instance of type

class Foo(object): version = "1.0.1" pass print(Foo.version) # 1.0.1 print(Foo.__name__) # Foo # ------------ # 使用type创建类是一样的 Foo = type("Foo", (object,), dict(version="1.0.1")) print(Foo.version) # 1.0.1 print(Foo.__name__) # Foo

Okay, back to "__new__" and "__call__"

In the metaclass, "__new__" will be executed when you define the class. It will only be executed once. If two classes use this metaclass, OK, it will be executed twice

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

And __call__ will be called every time you instantiate it. In fact, it is the same as Foo.__new__, which means that if your Foo defines __new__, the __call__ in the metaclass will not be executed

class FooMeta(type): def __new__(meta, name, bases, dct): print("metaclass __new__") return type(name, 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() print(f) # 输出结果 # metaclass __new__ # Foo __new__ # hello

The "__new__" of the metaclass can change the type you define. I defined Foo here as a list

# -*- coding: utf-8 -*- class FooMeta(type): def __new__(cls, name, bases, dct): return [1, 2, 3, 4] # 这里相当于执行 Foo = [1, 2, 3, 4] class Foo(): __metaclass__ = FooMeta print(Foo) # [1, 2, 3, 4] print(Foo[0:2]) # [1, 2] print(Foo.pop()) # 4

It’s very hard to write so much. There are very few people who can correctly understand metaclass. I don’t know how to thank the author of the question

    刘奇

    This is not a problem of metaclass affecting the priority of new and call, but that __new__ of metaclass will only be called once when creating a class. And this __new__ is used to create our class, as in your question. When was it created? When the interpreter interprets the Foo class, it will look for the __metaclass__ attribute in the class definition, and if it is found, use it to create the class. If not found, the built-in type will be used to create the class. Why not call it again? Because the metaclass has already created our class, we can't re-create the class every time Foo() is called. It can also be seen from your output that foo2 = Foo() does not output __new__. Because the class has been created by the metaclass at this time, you can just start using it directly. It is no different from the classes we usually use, so every time it is created, a new object is created. And why use __call__? Because Foo is a class created by the metaclass Singleton, you can think of Foo as an instance object of Singleton. So __call__ will be called every time Foo() is called. In __call__ we get the already created instance object. Isn't it just a singleton? .Singleton
    Let’s explain in detail through the code

    class Singleton(type): def __call__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): print(cls) cls._instance = super(Singleton, cls).__call__(*args, **kwargs) print(cls._instance) print('__call__') return cls._instance class Foo(): __metaclass__ = Singleton pass print('1') foo1 = Foo() print('2') foo2 = Foo() print (foo1 is foo2) # True

    After running, you will find that cls in the __call__ function is the class created by calling __new__ for the first and only time in the metaclass, which is Foo, and cls._instance is the instance object of our Foo. Every time Foo() is called, the same instance object is taken. Metaclasses are, after all, classes that create classes. Once created, the class is no different from the class you normally define.

      Latest Downloads
      More>
      Web Effects
      Website Source Code
      Website Materials
      Front End Template
      About us Disclaimer Sitemap
      php.cn:Public welfare online PHP training,Help PHP learners grow quickly!