2015-12-25 9 views
5

Viene generato un errore quando si tenta di impostare il valore di un argomento di una classe che eredita da str. L'errore si verifica solo quando provo ad accedere all'argomento con myClass(arg = 'test'). L'errore è:Impostare il valore di un argomento in una classe che eredita da int o float o str

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

La questione è mostrato in questa exemple:

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

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

solo l'ultima riga genera l'errore. La linea precedente funziona bene. Il problema è lo stesso con le classi che ereditano da int o da float.

UPDATE (soluzione):

ho trovato una soluzione con questi link:

La soluzione è:

class A(str): 

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

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

A(arg01= "test") 

Non so esattamente perché funzioni e indagherò su questo. Se qualcuno ha una spiegazione chiara, sono interessato e lo ringrazio in anticipo.

UPDATE (la mia spiegazione):

non sono sicuro affatto di ciò che sto per dire, ma che è quello che ho capito.

Immagina una classe 'myClass' senza alcuna eredità. Quando faccio questo myInstance = myClass(), ecco cosa succede:

Il metodo myClass.__new__ viene eseguito. Questo metodo creerà l'oggetto myInstance. __new__ è il vero costruttore (__init__ non è un costruttore!). In pseudocodice, __new__ simile a questa:

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. 

La situazione è un po 'diverso quando stiamo usando tipi immutabili (str, int, float, tuple). Nello pseudocodice precedente, ho scritto def __new__(cls, *args, **kwargs). Con tipi immutabili, lo pseudocodice per il metodo __new__ è più simile a questo def __new__(cls, anUniqueValue). Non capisco davvero perché il comportamento di immutableTypes.__new__ sia diverso da altri, ma è il caso. Potete vederlo in questo esempio:

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). 

Da lì, possiamo capire perché la soluzione presentata in precedenza funziona. Quello che facciamo è modificare il metodo __new__ di un tipo immutabile per funzionare proprio come un tipo mutabile.

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

Queste 2 linee convertono def __new__ (cls, anUniqueValue) a def __new__ (cls, *args, **kwargs)

Spero che la mia spiegazione è quasi chiaro e senza tanto errori.Se parli francese puoi saperne di più su questo link: http://sametmax.com/la-difference-entre-new-et-init-en-python/

+1

Probabilmente correlato a http://stackoverflow.com/questions/3748635/adding-optional-parameters-to-the-constructors-of-multiply-inheriting-subclasses anche se non sono sicuro di ciò che Alex Martelli intende esattamente con ' superclasse altrettanto arbitraria ». – timgeb

+1

@Morgan, la spiegazione è nella domanda collegata, potrebbe esserci anche una * lei * che può fornirti una spiegazione chiara;) –

+0

@Morgan non esitare a rispondere alla tua stessa domanda quando hai scoperto cosa sta succedendo esattamente sopra. Non ho compreso appieno le spiegazioni nelle domande collegate. – timgeb

risposta

2

Ciò che hai scritto è sostanzialmente giusto, ci sono alcuni errori qua e là.


class A(str): 

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

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

Questo non è del tutto corretto: se non si passa alcun argomento per str.__new__, la vostra nuova istanza sarà l'equivalente di una stringa vuota.

class A(str): 

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

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

In generale, __new__ e __init__ dovrebbe avere la stessa firma. Non è un requisito, ma in questo caso è necessario passare arg a str.__new__ quindi è necessario intercettare arg.


Procedimento myClass.__new__ viene eseguita. Questo metodo creerà l'oggetto myInstance. __new__ è il vero costruttore (__init__ non è un costruttore!). In pseudocodice, __new__ simile a questa:

Non è la responsabilità di __new__ chiamare __init__, come dimostrato da questo semplice esempio:

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__ 

Come si può vedere, nel mio __new__ I didn chiamare lo __init__ in modo esplicito e object.__new__ non chiama __init__.

__init__ viene chiamato automaticamente dall'interprete Python stesso ogni volta che __new__ restituisce un'istanza del tipo.


La situazione è un po 'diverso quando usiamo tipi immutabili (str, int, float, tupla).

Questo non è completamente vero. La situazione è diversa quando si eredita da un tipo che non utilizza l'implementazione __new__ predefinita.

L'implementazione di default __new__ (ad esempio object.__new__) è molto permissiva e ignora ogni argomento di posizione e argomento di parole chiave. Se lo sostituisci con un'implementazione meno permissiva, allora si verificano problemi come quello che stai osservando.

Che cosa è importante da capire è che il problema non è portato dal non predefinito __new__ in sé, ma per il fatto che il nostro __init__ non è compatibile con __new__.


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). 

avete ottenuto.Solo un po 'è sbagliato: la firma corretta per str.__new__ è:

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

Tutti gli argomenti tranne cls sono sia argomento posizionale e parola chiave. In effetti si può fare:

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

Vedete? Abbiamo utilizzato una firma per __init__ compatibile con str.__new__ e ora possiamo evitare di ignorare lo __new__!

+0

grazie per queste precisioni! – Morgan