2013-08-30 18 views
19

dire che io definisco la seguente classe:Costruttori di classi e argomenti delle parole chiave - In che modo Python determina quale è inatteso?

class MyClass(object): 
    def __init__(self, x, y): 
     self.x = x 
     self.y = y 

Normalmente, si potrebbe istanziare questa classe in uno dei seguenti modi:

>>> MyClass(1,2) 
<__main__.MyClass object at 0x8acbf8c> 
>>> MyClass(1, y=2) 
<__main__.MyClass object at 0x8acbeac> 
>>> MyClass(x=1, y=2) 
<__main__.MyClass object at 0x8acbf8c> 
>>> MyClass(y=2, x=1) 
<__main__.MyClass object at 0x8acbeac> 

Che è bene e dandy.

Ora, cerchiamo con un argomento chiave non valida e vedere cosa succede:

>>> MyClass(x=1, j=2) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() got an unexpected keyword argument 'j' 

Python solleva correttamente un errore di tipo e si lamenta del unexpected keyword argument 'j'.

Ora, possiamo provare con due argomenti chiave non validi:

>>> MyClass(i=1,j=2) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() got an unexpected keyword argument 'i' 

Avviso, che due dei keyword argomenti erano validi, ma Python si lamenta solo di uno di loro, 'i' in questo caso.

Consente invertire l'ordine degli argomenti a parola chiave non validi:

>>> MyClass(j=2, i=1) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() got an unexpected keyword argument 'i' 

Questo è interessante. Ho cambiato l'ordine degli argomenti delle parole chiave non valide, ma Python decide ancora di lamentarsi di 'i' e non di 'j'. Quindi Python ovviamente non sceglie semplicemente la prima chiave non valida di cui lamentarsi.

Proviamo un po 'di più:

>>> MyClass(c=2, i=1) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() got an unexpected keyword argument 'i' 
>>> MyClass(q=2, i=1) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() got an unexpected keyword argument 'i' 

In ordine alfabetico, ho provato una lettera prima e uno dopo ii, quindi Python non si lamenta in ordine alfabetico.

Qui ci sono alcuni di più, questa volta con i in prima posizione:

>>> MyClass(i=1, j=2) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() got an unexpected keyword argument 'i' 
>>> MyClass(i=1, b=2) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() got an unexpected keyword argument 'i' 
>>> MyClass(i=1, a=2) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __init__() got an unexpected keyword argument 'a' 

Aha! Mi sono lamentato di 'a' anziché di 'i'.

La mia domanda è, quando gli argomenti di parole chiave non validi sono dati a un costruttore di classi, come fa Python a determinare di quale lamentarsi?

+1

Come nota a margine, una chiamata di costruttore classe funziona esattamente allo stesso modo di una chiamata a una funzione (o metodo), quindi è possibile semplificare un po 'i test. – abarnert

+0

@abarnert Lo fanno? Sto ottenendo: 'def my_func (x, y): pass', quindi' my_func (i = 2, a = 1) 'si lamenta di' 'i'', mentre 'MyClass (i = 2, a = 1)' si lamenta di '' a''. – dg123

+0

Come descritto nella risposta di @MartijnPieters, il comportamento non è definito. Pertanto, i risultati dei test in pratica non sono necessariamente utili o applicabili. –

risposta

17

Gli argomenti delle parole chiave sono memorizzati in un dizionario e l'ordine del dizionario (ad esempio arbitrario, basato sull'algoritmo di hashing, le collisioni di hash e la cronologia di inserimento) si applica.

Per il primo campione, un dizionario con entrambe le i e j chiavi risultati in i essere elencati prima:

>>> dict(j=2, i=1) 
{'i': 1, 'j': 2} 

Si noti che il {...} letterale notazione dict inserisce le chiavi da destra a sinistra, mentre l'analisi parola chiave inserisce le parole chiave da sinistra a destra (questo è un dettaglio di implementazione CPython); da qui l'uso del costruttore dict() nell'esempio precedente.

Questo conta quando due chiavi hash allo stesso slot, come i e a:

>>> dict(i=1, a=2) 
{'a': 2, 'i': 1} 
>>> {'i': 1, 'a': 2} 
{'i': 1, 'a': 2} 

Dizionario ordine di uscita è fortemente dipendente l'inserimento e cancellazione storia e l'implementazione specifica Python; Python 3.3 ha introdotto un seed hash casuale per impedire un serio denial of service vector, il che significa che l'ordine del dizionario sarà radicalmente diverso anche tra i processi Python.

+0

L'ho pensato anch'io, ma ho paragonato 'MyClass (** {'i': 1, 'a': 2})' vs. 'MyClass (i = 1, a = 2)'. Il primo riporta 'i', mentre il secondo riporta' a'. Non dovrebbe essere la stessa cosa? – Claudiu

+0

Non c'è garanzia sull'ordinazione di un dizionario. Nessun risultato può essere generalizzato ... – user2722968

+1

@ Claudiu: L'ordine di iterazione per un dict è arbitrario. Quindi no, non puoi mai prevedere che sarà la stessa cosa. E se vuoi sapere perché esattamente è diverso in un caso particolare, oltre il semplice "è permesso essere diverso, e spesso lo è", devi scavare nei livelli più bassi dell'interprete e dell'oggetto dict. – abarnert

Problemi correlati