Czy jest jakiś powód, aby wybrać new zamiast init podczas definiowania metaklasy?

Zawsze ustawiałem metaklasy coś takiego:

class SomeMetaClass(type):
    def __new__(cls, name, bases, dict):
        #do stuff here

Ale właśnie natknąłem się na metaklasę, która została zdefiniowana Tak:

class SomeMetaClass(type):
    def __init__(self, name, bases, dict):
        #do stuff here

Czy Jest jakiś powód, by preferować jedno nad drugim?

Update : pamiętaj, że pytam o użycie __new__ i __init__ w metaklasie. Już rozumiem różnicę między nimi w innej klasie. Ale w metaklasie nie mogę użyć __new__ do implementacji buforowania, ponieważ {[2] } jest wywoływane tylko podczas tworzenia klasy w metaclass.

Author: Jason Baker, 2009-12-03

4 answers

Jeśli chcesz zmienić atrybuty dict przed utworzeniem klasy lub zmienić krotkę bases, musisz użyć __new__. Gdy __init__ zobaczy argumenty, obiekt klasy już istnieje. Ponadto, musisz użyć __new__, jeśli chcesz zwrócić coś innego niż nowo utworzona Klasa danego typu.

Z drugiej strony, do czasu __init__ działa, Klasa istnieje. Tak więc, możesz zrobić rzeczy takie jak dać odniesienie do właśnie utworzonej klasy do jednego z jej członków obiektów.

Edit : zmieniono sformułowanie, aby było bardziej jasne, że przez "obiekt" mam na myśli class-object.

 41
Author: Matt Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-03 15:47:22

Możesz zobaczyć pełny writeup w oficjalnym dokumencie , ale zasadniczo, {[0] } jest wywoływane przed nowy obiekt jest tworzony (w celu jego utworzenia) i __init__ jest wywoływane po nowy obiekt jest tworzony (w celu jego inicjalizacji).

Użycie __new__ pozwala na takie triki jak buforowanie obiektów (zwracanie zawsze tego samego obiektu dla tych samych argumentów zamiast tworzenia nowych) lub tworzenie obiektów innej klasy niż żądana (czasami używane do zwraca bardziej specyficzne podklasy żądanej klasy). Ogólnie rzecz biorąc, jeśli nie robisz czegoś dość dziwnego, __new__ ma ograniczoną użyteczność. Jeśli nie musisz powoływać się na takie oszustwa, trzymaj się __init__.

 3
Author: Ben Blank,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-03 15:16:57

Możesz zaimplementować buforowanie. Person("Jack") zawsze zwraca nowy obiekt w drugim przykładzie, podczas gdy można wyszukać istniejącą instancję w pierwszym przykładzie za pomocą __new__ (lub nie zwracać niczego, jeśli chcesz).

 1
Author: Otto Allmendinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-03 15:05:44

Jak już zostało powiedziane, jeśli zamierzasz zmienić coś takiego jak klasy bazowe lub atrybuty, musisz to zrobić w __new__. To samo dotyczy name klasy, ale wydaje się, że jest z tym osobliwość. Po zmianie name nie jest ona propagowana do __init__, mimo że np. attr jest.

Więc będziesz miał:

class Meta(type):
    def __new__(cls, name, bases, attr):
        name = "A_class_named_" + name
        return type.__new__(cls, name, bases, attr)

    def __init__(cls, name, bases, attr):
        print "I am still called '" + name + "' in init"
        return super(Meta, cls).__init__(name, bases, attr)

class A(object):
    __metaclass__ = Meta

print "Now I'm", A.__name__

Druki

I am still called 'A' in init
Now I'm A_class_named_A
Jest to ważne, aby wiedzieć, jeśli __init__ wywoła super metaklasę, która robi dodatkową magię. W takim przypadku, jeden ma aby ponownie zmienić nazwę przed wywołaniem super.__init__.
 1
Author: Debilski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-11 21:24:27