Pertanyaan Amankah membuat instance kelas di metaclass dengan Python?


Seberapa aman untuk membuat instance dari kelas di konstruktor metaclass (__new__ dan __init__)? Saya secara khusus tertarik untuk Python 2.7, tetapi apa yang dilakukan Python 3 juga diterima.

Itu Dokumen model data Python kedengarannya seperti ditulis untuk kasus pembuatan instance kelas normal, dan saya tidak begitu yakin bagaimana aturannya bisa sangat berbeda ketika terjadi di metaclass.

Sebagai contoh, katakanlah saya memiliki kode seperti ini:

class Meta(type):
  NEWED = []
  INITED_BEFORE = []
  INITED_AFTER = []
  def __new__(meta, name, bases, dict):
    cls = super(Meta, meta).__new__(meta, name, bases, dict)
    instance = cls()
    Meta.NEWED.append(instance)
    return cls

  def __init__(cls, name, bases, dict):
    Meta.INITED_BEFORE.append(cls())
    super(Meta, cls).__init__(name, bases, dict)
    Meta.INITED_AFTER.append(cls())

class Foo(object):
  __metaclass__ = Meta

Pada titik mana, jika ada, apakah aman untuk membangun sebuah instance Foo sementara metaclass membangunnya, dan jenis peringatan apa yang ada di sana?

Salah satu kecurigaan yang saya miliki adalah bahwa, jika Foo mewarisi kelas lain, atau subkelas, dan kelas-kelas lain memiliki metaclass mereka sendiri, kemudian menelepon cls() di salah satu metode metaclass akan memanggilnya pada objek kelas yang belum selesai. Benarkah?


4
2018-05-06 18:00


asal


Jawaban:


Salah satu kecurigaan yang saya miliki adalah bahwa, jika Foo mewarisi kelas lain, atau subkelas, dan kelas-kelas lain memiliki metaclass mereka sendiri, kemudian memanggil cls () dalam salah satu metode metaclass akan memanggilnya pada objek kelas yang belum selesai . Benarkah?

Itu benar, sejauh di metaclass's __new__, itu __init__ belum akan dipanggil. Metaclass Anda memiliki __init__, seperti yang mungkin subkelas, jadi Anda harus memastikan yang dipanggil lebih dulu. Jadi saya tidak akan mencoba membuat instance dari kelas yang belum sepenuhnya digunakan.

Satu hal yang mungkin Anda lakukan adalah memanggil metaclass secara manual __init__ dari dalam __new__. Tapi Anda harus mengatur bendera atau sesuatu __init__ untuk memastikan bahwa kelas tidak mendapat __init__dua kali. Mungkin ada cara yang lebih pintar juga yang tidak saya pikirkan saat ini.


0
2018-05-06 18:21