2015-12-25 13 views
5

Ein Fehler wird ausgelöst, wenn ich den Wert eines Arguments einer Klasse festlegen, die von str erbt. Der Fehler tritt nur auf, wenn ich versuche, mit myClass(arg = 'test') auf das Argument zuzugreifen. Der Fehler ist:Legen Sie den Wert eines Arguments in einer Klasse fest, die von int oder float oder str erbt

TypeError: 'arg' is an invalid keyword argument for this function 

Das Problem wird in diesem exemple gezeigt:

class A(str):  
    def __init__(self, arg): 
     pass 

A("test") # Works well 
A(arg = "test") # Fails 

Nur die letzte Zeile der Fehler auslöst. Die vorherige Zeile funktioniert gut. Das Problem ist dasselbe mit Klassen, die von int oder float erben.

UPDATE (Lösung):

fand ich eine Lösung mit diesen Links:

Die Lösung lautet:

class A(str): 

    def __new__(cls, *args, **kwargs): 
     return str.__new__(cls) 

    def __init__(self, arg01): 
     print(arg01) 

A(arg01= "test") 

Ich weiß nicht genau, warum das funktioniert und ich werde es untersuchen. Wenn jemand eine klare Erklärung hat, bin ich interessiert und ich danke ihm im Voraus.

UPDATE (meine Erklärung):

Ich bin überhaupt nicht sicher, was ich will sagen, aber das ist, was ich verstanden.

Stellen Sie sich eine Klasse 'myClass' ohne irgendeine Vererbung vor. Wenn ich dies tun myInstance = myClass(), passiert Folgendes:

Die Methode myClass.__new__ wird ausgeführt. Diese Methode erstellt das Objekt myInstance. __new__ ist der echte Konstruktor (__init__ ist kein Konstruktor!). In Pseudo-Code, schauen __new__ etwas wie folgt aus:

def __new__ (cls, *args, **kwargs): 

    myInstance = # Do some stuff to create myInstance, an object of the type passed as argument (cls). 
    # Add the method __init__ to myInstance. 
    # Call __init__ and pass to it the list 'args' and the dictionary 'kwargs' (both come from arguments of __new__). Pass to it also the object itself (self) : 
    obj.__init__(self, args, kwargs) : 
     # Do some stuff. 

Die Situation ist ein wenig anders, wenn wir unveränderliche Typen verwenden (str, int, float, Tupel). Im vorherigen Pseudocode habe ich def __new__(cls, *args, **kwargs) geschrieben. Bei unveränderlichen Typen ist der Pseudocode für die Methode __new__ eher def __new__(cls, anUniqueValue). Ich verstehe nicht wirklich, warum das Verhalten von immutableTypes.__new__ von anderen abweicht, aber es ist der Fall. Sie können es in diesem exemple sehen:

class foo(): 

    def __init__(self): 
     pass 

foo.__new__(foo, arg = 1) 
# That works because the method __new__ look like this : def __new__(*args, **kargs). 

str.__new__(str, arg = 1) 
# That fails because we are trying to pass the attribute 'arg' to a method which look like this : def __new__(anUniqueValue). 

Von dort aus können wir verstehen, warum die Lösung früheren Arbeiten präsentiert. Was wir tun, ist die Bearbeitung der Methode __new__ eines unveränderlichen Typs, der genau wie ein veränderbarer Typ funktioniert.

def __new__(cls, *args, **kwargs): 
    return str.__new__(cls) 

Diese 2 Zeilen konvertieren def __new__ (cls, anUniqueValue) zu def __new__ (cls, *args, **kwargs)

ich meine Erklärung hoffen fast klar und ohne so viel Fehler.Wenn Sie Französisch sprechen, können Sie mehr über diesen Link erfahren: http://sametmax.com/la-difference-entre-new-et-init-en-python/

+1

Möglicherweise im Zusammenhang mit http://StackOverflow.com/Questions/3748635/Adding-optional-Parameters-to-the-Constructors-of-multiply-inheriting-subclasses, obwohl ich nicht sicher bin, was Alex Martelli genau durch " ebenso willkürliche Oberklasse '. – timgeb

+1

@Morgan, die Erklärung ist in der verknüpften Frage, kann es auch eine * sie *, die Sie mit einer klaren Erklärung bieten können;) –

+0

@Morgan zögern Sie nicht, Ihre eigene Frage zu beantworten, wenn Sie herausgefunden haben, was genau geht auf. Ich habe die Erklärungen in den verlinkten Fragen nicht vollständig verstanden. – timgeb

Antwort

2

Was Sie geschrieben haben, ist im Wesentlichen richtig, es gibt ein paar Fehler hier und da.


class A(str): 

    def __new__(cls, *args, **kwargs): 
     return str.__new__(cls) 

    def __init__(self, arg01): 
     print(arg01) 

Das ist nicht ganz richtig: Wenn Sie kein Argument zu str.__new__, Ihre neue Instanz wird das Äquivalent eines leeren String tun.

class A(str): 

    def __new__(cls, arg): 
     return str.__new__(cls, arg) 

    def __init__(self, arg): 
     print(arg) 

Im Allgemeinen __new__ und __init__ sollte die gleiche Signatur haben. Es ist keine Voraussetzung, aber in diesem Fall müssen Sie arg zu str.__new__ übergeben, so dass Sie arg abfangen müssen.


Verfahren myClass.__new__ ausgeführt. Diese Methode erstellt das Objekt myInstance. __new__ ist der echte Konstruktor (__init__ ist kein Konstruktor!). In Pseudo-Code, schauen __new__ etwas wie folgt aus:

Es ist nicht in der Verantwortung von __new__ ist __init__ zu nennen, wie sie durch dieses einfache Beispiel demonstriert:

class C: 

    def __new__(cls): 
     print('entering __new__') 
     self = super().__new__(cls) 
     print('exiting __new__') 
     return self 

    def __init__(self): 
     print('entering __init__') 
     super().__init__() 
     print('exiting __init__') 


C() 

# Output: 
# entering __new__ 
# exiting __new__ 
# entering __init__ 
# exiting __init__ 

Wie Sie sehen können, in meinem __new__ I didn Rufen Sie __init__ explizit nicht an, und object.__new__ ruft __init__ nicht auf.

__init__ wird automatisch vom Python-Interpreter selbst aufgerufen, wenn __new__ eine Instanz des Typs zurückgibt.


Die Situation ist ein wenig anders, wenn wir unveränderliche Typen verwenden (str, int, float, Tupel).

Dies ist nicht ganz richtig. Die Situation ist anders , wenn wir von einem Typ erben, der die Standard __new__ Implementierung nicht verwendet.

Die Standardimplementierung __new__ (d. H. object.__new__) ist sehr freizügig und ignoriert jedes Positionsargument und Schlüsselwortargument. Wenn Sie es durch eine weniger permissive Implementierung ersetzen, dann passieren Probleme wie die, die Sie beobachten.

Was wichtig zu verstehen, ist, dass das Problem nicht durch die Nicht-Standard-__new__ gebracht wird selbst, sondern durch die Tatsache, dass unsere __init__ ist nicht mit __new__ kompatibel.


foo.__new__(foo, arg = 1) 
# That works because the method __new__ look like this : def __new__(*args, **kargs). 
str.__new__(str, arg = 1) 
# That fails because we are trying to pass the attribute 'arg' to a method which look like this : def __new__(anUniqueValue). 

Du hast es.ein nur wenig falsch ist: die richtige Signatur für str.__new__ ist:

def __new__(cls[, object[, encoding[, errors]]]) 

Alle Argumente außer cls sind beide Lage- und Keyword-Argument. Tatsächlich können Sie tun:

>>> class C(str): 
...  def __init__(self, object): 
...   pass 
... 
>>> C('abc') 
'abc' 
>>> C(object='abc') 
'abc' 

Sie sehen? Wir haben eine Signatur für __init__ verwendet, die kompatibel ist mit str.__new__ und jetzt können wir vermeiden, dass __new__ überschrieben wird!

+0

danke für diese Präzisionen! – Morgan