2016-04-27 5 views
2

Was ich gelungen, so weit zu tun:

Ich habe eine elem Klasse gemacht HTML-Elemente zu repräsentieren (div, html , span, body usw.).Mix-Typ() und benutzerdefinierte __init __() mit super() .__ init __()

Ich bin in der Lage, diese Klasse so derivatisieren Subklassen für jedes Element zu machen:

class elem: 
    def __init__(self, content="", tag="div", attr={}, tag_type="double"): 
     """Builds the element.""" 
     self.tag = tag 
     self.attr = attr 
     self.content = content 
     self.tag_type = tag_type 

class head(elem): 
    """A head html element.""" 

    def __init__(self, content=None, **kwargs): 
     super().__init__(tag="head", content=content, **kwargs) 

Und es funktioniert ziemlich gut.

Aber ich muss dies für jede Unterklasse Deklaration schreiben, und das ist ziemlich wiederholend und redundant, wenn ich jeden HTML-Tag-Typ machen will.

Also habe ich versucht, eine make_elem() Funktion zu machen, die meine Klasse machen würde, indem ich den entsprechenden Tag-Namen als String-Parameter nehme.

So anstelle der bisherigen Klassendefinition, würde ich einfach so etwas wie dieses:

head = make_elem_class("head") 

Wo ich bin stecken

Diese Funktion eine Klasse erstellen sollte. Und die __init__()-Methode aus dieser Klasse sollte die __init__()-Methode aus der Klasse aufrufen, von der sie erbt.

Ich habe versucht, diese make_elem_class() Funktion zu machen, und es sah wie folgt aus:

def make_elem_class(name): 
    """Dynamically creates the class with a type() call.""" 

    def init(self, content=None, **kwargs): 
     super().__init__(tag=name, content=None, **kwargs) 

    return type(name, (elem,), {"__init__" : init}) 

Aber wenn html = make_elem_class('html') läuft, dann html("html element") ich die folgende Fehlermeldung erhalten:

Traceback (most recent call last): 
    File "elements.py", line 118, in <module> 
    html("html element") 
    File "elements.py", line 20, in init 
    super().__init__(tag=name, content=None, **kwargs) 
TypeError: object.__init__() takes no parameters 

Ich denke, dass es etwas mit dem leeren super() Anruf zu tun, so versuchte ich stattdessen mit super(elem, self). Aber es funktioniert offensichtlich nicht besser.

Wie konnte ich das erreichen?

NB: Wenn ich die "__init__":init vom Dictionnary im type() Anruf zu entfernen, es funktioniert gut, aber der Tag ist nicht richtig eingestellt in meinem Elem. Ich habe auch versucht, {"tag":name} zu type() direkt zu übergeben, aber es hat auch nicht funktioniert.

+0

Können Sie bitte auch eine vereinfachte 'elem' Klasse erstellen? Ich kann Ihre genaue Ausnahme auch nicht reproduzieren, stattdessen bekomme ich 'RuntimeError: super(): __class__ cell not found'. Benutzt du 'make_elem_class' vielleicht irgendwo als statische oder Klassenmethode? –

+0

Oder hast du vielleicht '__class__ = elem' irgendwo gesetzt? –

+0

@MartijnPieters Sicher! Ich habe meinen Beitrag bearbeitet, die '__init __()' Methode ist abgeschlossen. Und kein '__class__ = elem' in meinem Code. – vmonteco

Antwort

3

Sie können hier das Formular ohne Argumente von super() nicht verwenden, da es hier keine class-Anweisung gibt, um den Kontext bereitzustellen, den diese Funktion normalerweise benötigt.

Oder eher, Sie können nicht, es sei denn, Sie stellen diesen Kontext selbst zur Verfügung; Sie müssen den Namen __class__ als Verschluss setzen hier:

def make_elem_class(name): 
    """Dynamically creates the class with a type() call.""" 

    def init(self, content=None, **kwargs): 
     super().__init__(tag=name, content=content, **kwargs) 

    __class__ = type(name, (elem,), {"__init__" : init}) 
    return __class__ 

super() automatisch den __class__ Wert aus dem Verschluss nehmen. Beachten Sie, dass ich den Wert für content, nicht None, an die Methode elem.__init__ weitergebe; Du würdest diesen Wert nicht verlieren wollen.

Wenn dies too magical für Sie ist, benennen Sie die Klasse explizit und self beim Aufruf super(); wieder, wird die Klasse von der Schließung genommen werden:

def make_elem_class(name): 
    """Dynamically creates the class with a type() call.""" 

    def init(self, content=None, **kwargs): 
     super(elemcls, self).__init__(tag=name, content=content, **kwargs) 

    elemcls = type(name, (elem,), {"__init__" : init}) 
    return elemcls 
+0

Ihre Lösungen funktionieren perfekt! Es mag vereinfacht sein (ich bin kein Python-Experte und habe vielleicht nicht das richtige Vokabular), aber 'super()' sucht nach dem Klassennamen, den wir ihm im lokalen Bereich übergeben ('__class__', wenn kein Argument vorhanden ist wird übergeben, und '__class__' wird erstellt, wenn eine' class myclass(): 'syntaxe?) verwendet wird? – vmonteco

+0

@vmonteco: siehe bereits verlinkt [Warum ist Python 3.x super() Magie?] (Https://stackoverflow.com/q/19608134); 'super()' sucht nach einer * Schließzelle *, und diese Schließzelle wird automatisch vom Python-Compiler bereitgestellt, wenn Sie a) eine Methode in einem 'class' Block erstellen und' b' den Namen 'super' in verwenden diese Methode. –

+0

@vmonteco: Normalerweise werden Schließzellen nur erstellt, wenn Sie einen Namen aus einem übergeordneten Funktionsbereich verwenden (wie 'name' in Ihrer' make_elem_class', um in der 'init()' -Funktion eine solche Zelle verwenden zu können erstellt). –

1

Was ist über eine straight-forward-Lösung wie Folgern das Tag für die Klasse __name__?

class elem: 
    def __init__(self, content="", tag=None, attr={}, tag_type="double"): 
     """Builds the element.""" 
     self.tag = tag or self.__class__.__name__ 
     ... 

Und dann:

class div(elem): pass 
class head(elem): "Optional docstring for <head>" 
... 

Ein bisschen weniger Magie (umstritten) und ein wenig deutlicher. :-)

+0

Nicht sicher, dass das weniger Magie ist, ehrlich gesagt. Und wir haben nur Glück, dass es keine HTML-Tags gibt, die [reservierte Python-Schlüsselwörter] sind (https://docs.python.org/3/reference/lexical_analysis.html#keywords). Mit einem Klassenattribut könnte die bessere Idee sein, dass "self.tag" dann von "elem.tag" liest, was Sie in einer Unterklasse überschreiben würden. Einfaches Setzen mit ''tag': name' im Dictionary, übergeben an' type() '. –

+0

Fair genug!Nicht wirklich weniger Magie, wahrscheinlich, eher eine Art von eingebettetem DSL für diese Art von Dingen. –

0

Ich denke, das ist ein kleines XY-Problem. Darin haben Sie gefragt, wie Sie super in einer dynamisch erstellten Klasse verwenden, aber was Sie wirklich wollen, ist eine weniger ausführliche Möglichkeit, verschiedene Klassenvariablen und Standardwerte für Ihre Unterklassen festzulegen.

Da Sie nicht erwarten, dass alle Instanzen derselben Tag-Klasse denselben Tag-Namen verwenden, können Sie sie genauso gut als Klassenvariable als als Instanzvariable festlegen. z.B.

from abc import ABC, abstractmethod 

class Elem(ABC): 
    tag_type = "double" # the default tag type 

    def __init__(self, content="", attr=None, tag_type=None): 
     """Builds the element.""" 
     self.attr = attr if attr is not None else {} 
     self.content = content 
     if tag_type is not None: 
      self.tag_type = tag_type 

    @property 
    @abstractmethod 
    def tag(self): 
     """All base classes should identify the tag they represent""" 
     raise TypeError("undefined tag for {}".format(type(self))) 

class Head(Elem): 
    tag = "head" 
    tag_type = "text" 

class Div(Elem): 
    tag = "div" 

h = Head() 
d = Div() 
h1 = Head(tag_type="int") 

assert h.tag == "head" 
assert d.tag == "div" 
assert h1.tag == "head" 
assert h.tag_type == "text" 
assert d.tag_type == "double" 
assert h1.tag_type == "int" 

Sie können jetzt sehr kurze untergeordnete Klassen schreiben und Ihre Klassen trotzdem explizit deklarieren lassen. Sie werden feststellen, dass ich einige der Standardeinstellungen auf None geändert habe. Für attr liegt das daran, dass veränderbare Standardargumente nicht so funktionieren, wie Sie erwarten - es verhält sich eher wie eine gemeinsam genutzte Klassenvariable. Verwenden Sie stattdessen den Standardwert None, wenn attr nicht angegeben wurde, und erstellen Sie für jede Instanz eine neue attr. Die zweite (tag_type) ist so, dass, wenn tag_type angegeben ist, die Instanz ihre tag_type gesetzt hat, aber alle anderen Instanzen verlassen sich auf die Klasse für den Standardwert.

Verwandte Themen