2013-08-30 11 views
19

Say I zdefiniować następujące klasy:Konstruktory klas i argumenty słów kluczowych - w jaki sposób Python określa, który z nich jest nieoczekiwany?

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

Normalnie jeden instancji tej klasy w jeden z następujących sposobów:

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

który jest tylko cacy.

Teraz spróbujemy z nieprawidłowym kluczowego argumentu i zobaczyć, co się dzieje:

>>> 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 słusznie podnosi typu błędu i skarży się na unexpected keyword argument 'j'.

Teraz możemy spróbować z dwoma argumentami nieważnych słów kluczowych:

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

odnotować, że dwa z kluczowych argumentów były nieważne, ale Python narzeka tylko o jednym z nich, 'i' w tym przypadku.

Pozwala odwrócić kolejność nieprawidłowych argumentów słowa kluczowego:

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

który jest interesujący. Zmieniłem kolejność nieprawidłowych argumentów słów kluczowych, ale Python nadal decyduje się na skargę na temat 'i', a nie 'j'. Python oczywiście nie wybiera po prostu pierwszego nieprawidłowego klucza, na który można narzekać.

Spróbujmy trochę więcej:

>>> 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' 

alfabetycznie Próbowałem nas przed i i jeden po i, więc Python nie narzeka alfabetycznie.

Oto kilka, tym razem z i w pierwszej pozycji:

>>> 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! Mam to narzekać na 'a' zamiast 'i'.

Moje pytanie brzmi, kiedy niepoprawne argumenty słów kluczowych są podawane konstruktorowi klasy, w jaki sposób Python określa, na który z nich narzekać?

+1

Na marginesie, wywołanie konstruktora klasy działa dokładnie tak samo, jak wywołanie funkcji (lub metody), co pozwala nieco uprościć testy. – abarnert

+0

@abarnert Czy oni? Otrzymuję: 'def my_func (x, y): pass', następnie' my_func (i = 2, a = 1) 'narzeka na' 'i'', podczas gdy 'MyClass (i = 2, a = 1)' narzeka na "a". – dg123

+0

Jak opisano w odpowiedzi @MartijnPieters, zachowanie jest niezdefiniowane. Dlatego wyniki testów w praktyce niekoniecznie są użyteczne lub mają zastosowanie. –

Odpowiedz

17

Argumenty dotyczące słów kluczowych są przechowywane w słowniku, a kolejność słowników (na przykład arbitralnie, w oparciu o algorytm mieszania, kolizje haszowania i historię wstawiania) ma zastosowanie.

Podczas pierwszej próby, słownik z obu i i j klawiszy powoduje i jest wymieniony jako pierwszy:

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

Zauważ, że dosłowny zapis DICT {...} wkłada kluczyki od prawej do lewej, podczas analizowania słów kluczowych wstawia słowa kluczowe od lewej do prawej (jest to szczegół implementacji CPython); stąd użycie konstruktora dict() w powyższym przykładzie.

To ważne, jeśli dwa klawisze hash do tego samego gniazda, jak i i a:

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

słownik kolejność wyjścia jest silnie uzależniona od wstawiania i usuwania historii i konkretnej implementacji Pythona; Python 3.3 wprowadził losowe hash, aby zapobiec poważnemu denial of service vector, co oznacza, że ​​porządek słownika będzie radykalnie różnił się nawet między procesami Pythona.

+0

Też tak pomyślałem, ale porównaj 'MyClass (** {'i': 1, 'a': 2})' vs. 'MyClass (i = 1, a = 2)'. Pierwsze raporty "i", podczas gdy drugie raporty "a". Czy nie powinno to być to samo? – Claudiu

+0

Nie ma gwarancji na zamawianie słownika. Żadnego wyniku nie można uogólnić ... – user2722968

+1

@Claudiu: Kolejność iteracji dla dyktatu jest arbitralna. Więc nie, nie możesz przewidzieć, że to będzie to samo. A jeśli chcesz wiedzieć, dlaczego w konkretnym przypadku jest inaczej, poza tym, że "może być inaczej i często jest", musisz zagłębić się w niższe poziomy interpretera i obiektu dyktującego. – abarnert

Powiązane problemy