2015-06-23 6 views
6

So habe ich diese Metaklasse, die ich für die automatische Registrierung von neuen Komponenten, dh Unterklassen einer Basiskomponente verwenden möchte Klasse. Beim Registrieren einer neuen Komponente wird erwartet, dass die Instanz an die register_component()-Funktion übergeben wird, die das verarbeitet.Das Instanziieren einer gerade erstellten Klasse in einer Metaklasse führt zu einem RuntimeError ("super(): empty __class__ cell")

Metaclass Code (abgespeckte Version):

class AutoRegisteredMeta(type): 
    def __new__(metacls, name, bases, attrs): 

     # ... (omitted) check if "name" has already been registered ... 

     new_class = super().__new__(metacls, name, bases, attrs) 
     register_component(name, new_class()) # RuntimeError(super(): empty __class__ cell) 
     return new_class 

Das Problem ist, dass new_class() führt zu einem Fehler aufgerufen wird - aber nicht für alle Klassen. Nach einigen Experimenten wurde mir klar, dass dies nur passiert, wenn eine Unterklasse super().__init__() in ihrer eigenen __init__() Methode aufruft.

Probenkomponente und die Basisklasse:

class BaseComponent(metaclass=AutoRegisteredMeta): 
    def __init__(self): 
     # do some work here ... 

class ComponentFoo(BaseComponent): 
    def __init__(self): 
     super().__init__() # <--- RuntimeError occurs here 
     self.foo = 'bar' 

Was mache ich hier falsch? Reading this Ich fand heraus, dass ich wahrscheinlich nicht Instanziierung in metaclass'es __new__() oder __init__() tun sollte, richtig? Kann das vielleicht irgendwie umgangen werden?

Auch einige Erklärung in Laien wäre nett, ich weiß nicht viel von den Interna der CPython-Implementierung.

Vielen Dank im Voraus!

(FWIW, verwende ich Python 3.3.6, Ubuntu)


EDIT: Ich füge die minimale Beispiel, das angefordert wurde, können Sie es direkt ausführen und die Fehler in Aktion selbst sehen.

#!/usr/bin/env python3 


class AutoRegisteredMeta(type): 
    def __new__(metacls, name, bases, attrs): 
     new_class = super().__new__(metacls, name, bases, attrs) 
     new_class() # <--- RuntimeError can occur here 
     return new_class 


class BaseComponent(metaclass=AutoRegisteredMeta): 
    def __init__(self): 
     print("BaseComponent __init__()") 


class GoodComponent(BaseComponent): 
    def __init__(self): 
     print("GoodComponent __init__()") 


class BadComponent(BaseComponent): 
    def __init__(self): 
     print("BadComponent __init__()") 
     super().__init__() # <--- RuntimeError occurs because of this 
+2

Ihr eigentlich Registrierungscode eine Instanz der Klasse instanziiert, bevor die Metaklasse beendet hat. 'register_component (name, new_class)' könnte besser sein. –

+0

Könnten Sie das in ein [minimales Beispiel] (http://stackoverflow.com/help/mcve) reorganisieren, das tatsächlich ausgeführt wird (einschließlich 'register_component',' BaseComponent .__ init__', ...)? – jonrsharpe

+0

@CharlieClark Ja, ich weiß, das ist genau das Problem. Das ließ mich denken, dass es besser wäre, die Klasse selbst als ihre Instanz zu registrieren, was auch Sie in Ihrem Kommentar vorgeschlagen haben. Die Registrierung einer Komponenteninstanz entspricht der aktuellen Vorgehensweise im Rest des Systems. :) – plamut

Antwort

3

vielleicht dies funktioniert:

#!/usr/bin/env python3 


class AutoRegisteredMeta(type): 
    def __new__(metacls, name, bases, attrs): 
     new_class = super().__new__(metacls, name, bases, attrs) 
     new_class() 
     return new_class 


class BaseComponent(metaclass=AutoRegisteredMeta): 
    def __init__(self): 
     print("BaseComponent __init__()") 


class GoodComponent(BaseComponent): 
    def __init__(self): 
     print("GoodComponent __init__()") 


class BadComponent(BaseComponent): 
    def __init__(self): 
     print("BadComponent __init__()") 
     super(self.__class__, self).__init__() 
+0

Ich habe es versucht und es funktioniert, danke. Das Hinzufügen von expliziten Argumenten zu "super()", d. H. "Self .__ class__" und "self", führte zum Verschwinden des Problems. Akzeptieren Sie diese Antwort. Übrigens möchten Sie möglicherweise den letzten Kommentar in der Quelle entfernen, da dieser nicht mehr zutrifft. – plamut

+0

Ich habe es gerade recherchiert – svs

+0

BTW, nicht 'super (self .__ class__, self) .__ init __()', da es zu unendlichen Schleifen führen kann - 'self' könnte eine Instanz einer Unterklasse sein, und mit' super() 'wie dies kann die' __init __() 'Methode, die diese' super() 'Anweisung enthält, immer und immer wieder aufrufen. – plamut

Verwandte Themen