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

参考文档

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

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

大家讲道理
大家讲道理

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

membalas semua(2)
黄舟

Saya akan mengubah suainya di sini dan menerangkannya dengan teliti

Metaclass mentakrifkan atribut struktur kelas, dan kaedah "__new__" dan "__init__" dalam kelas digunakan untuk memproses kejadian kelas

Setiap kelas yang kami takrifkan boleh difahami sebagai contoh jenis

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

Baiklah, kembali kepada "__baru__" dan "__panggilan__"

Dalam metaclass, "__new__" akan dilaksanakan apabila anda mentakrifkan kelas Ia hanya akan dilaksanakan sekali Jika dua kelas menggunakan metaclass ini, OK, ia akan dilaksanakan dua kali

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 并没什么错, 因为本来默认就是这样

Dan __call__ akan dipanggil setiap kali anda membuat instantiat, sebenarnya, ia adalah sama dengan Foo.__new__, yang bermaksud jika Foo anda mentakrifkan __new__, __call__ dalam metaclass tidak akan Laksanakan

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

"__baru__" kelas meta boleh menukar jenis yang anda takrifkan di sini sebagai senarai

# -*- 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

Sangat sukar untuk menulis begitu banyak. Terdapat sangat sedikit orang yang dapat memahami metaclass dengan betul. Saya tidak tahu bagaimana untuk berterima kasih kepada saya kerana bertanyakan soalan itu

刘奇

Ini bukan masalah metaclass yang menjejaskan keutamaan new dan call, tetapi __new__ metaclass itu hanya akan dipanggil sekali apabila mencipta kelas. Dan __new__ ini digunakan untuk mencipta kelas kami, seperti dalam soalan anda. Bilakah ia dicipta? Apabila jurubahasa mentafsir kelas Foo, ia akan mencari atribut __metaclass__ dalam definisi kelas, dan jika ia ditemui, gunakannya untuk mencipta kelas. Jika tidak ditemui, jenis terbina dalam akan digunakan untuk mencipta kelas. Mengapa tidak memanggilnya semula? Kerana metaclass telah mencipta kelas kami, kami tidak boleh mencipta semula kelas setiap kali Foo() dipanggil. Ia juga boleh dilihat daripada output anda bahawa foo2 = Foo() tidak mengeluarkan __new__. Kerana pada masa ini kelas telah dicipta oleh metaclass, iaitu, Singleton, anda boleh mula menggunakannya secara langsung. Ia tidak berbeza dengan kelas yang biasa kita gunakan, jadi setiap kali ia dicipta, objek baru dicipta. Dan mengapa menggunakan __call__?
Kerana Foo ialah kelas yang dicipta oleh metaclass Singleton, anda boleh menganggap Foo sebagai objek contoh Singleton. Jadi __call__ akan dipanggil setiap kali Foo() dipanggil. Dalam __call__ kita mendapat objek contoh yang telah dibuat. Bukankah ia hanya singleton? .

Mari jelaskan secara terperinci melalui kod

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

Selepas berjalan, anda akan mendapati bahawa cls dalam fungsi __call__ ialah kelas yang dicipta dengan memanggil __new__ untuk kali pertama dan satu-satunya dalam metaclass, iaitu Foo, dan cls._instance ialah objek contoh Foo kami. Setiap kali Foo() dipanggil, objek contoh yang sama diperoleh. Metaclass adalah, selepas semua, kelas yang mencipta kelas. Setelah dibuat, kelas itu tidak berbeza daripada kelas yang biasa anda tentukan.

Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan