Widzę takie wzory
def __init__(self, x, y, z):
...
self.x = x
self.y = y
self.z = z
...
dość często, często z dużo większą liczbą parametrów. Czy istnieje dobry sposób na uniknięcie tego typu żmudnej powtarzalności? Czy namedtuple
zamiast tego klasa powinna dziedziczyć ?
__slots__
do tego celu ; jest nieco niepytoniczny (bardziej rozwlekły, aby zaoszczędzić pamięć), ale w dużej mierze podoba mi się to, aby uniknąć ryzyka automatycznego ożywienia zupełnie nowego atrybutu, jeśli wpiszę nazwę.ini <shortcut> x, y, z): <shortcut>
i gotowe.Odpowiedzi:
Edycja: jeśli masz Pythona 3.7+, po prostu użyj klas danych
Rozwiązanie dekoracyjne, które zachowuje podpis:
źródło
signature
Zastrzeżenie: Wygląda na to, że kilka osób jest zaniepokojonych przedstawieniem tego rozwiązania, więc przedstawię bardzo jasne zastrzeżenie. Nie powinieneś używać tego rozwiązania. Podaję je tylko jako informacje, więc wiesz, że język jest do tego zdolny. Reszta odpowiedzi to po prostu pokazanie możliwości językowych, a nie popieranie ich w ten sposób.
Nie ma nic złego w jawnym kopiowaniu parametrów do atrybutów. Jeśli masz zbyt wiele parametrów w ctor, czasami jest to uważane za zapach kodu i być może powinieneś zgrupować te parametry w mniejszą liczbę obiektów. Innym razem jest to konieczne i nie ma w tym nic złego. W każdym razie robienie tego wyraźnie jest drogą do zrobienia.
Ponieważ jednak pytasz JAK można to zrobić (a nie czy należy to zrobić), to jedno rozwiązanie jest takie:
źródło
self.__dict__.update(kwargs)
może być nieco bardziej pytonicznaA.__init__
faktycznie oczekuje i nie ma sprawdzania błędów pod kątem błędnie wpisanych nazw argumentów.kwargs
powoduje otwarcie odpowiednika ataku polegającego na iniekcji SQL. Jeśli twój obiekt ma metodę o nazwiemy_method
i przekazujesz argument o nazwiemy_method
do konstruktora, toupdate()
słownik, po prostu nadpisałeś metodę.Jak wspominali inni, powtarzalność nie jest zła, ale w niektórych przypadkach nazwana dwukrotność może świetnie pasować do tego typu problemów. Pozwala to uniknąć używania locals () lub kwargs, które zwykle są złym pomysłem.
Znalazłem ograniczone zastosowanie, ale możesz dziedziczyć nazwany tuple, jak w przypadku każdego innego obiektu (kontynuacja przykładu):
źródło
properties
metodę można zapisać jako justreturn tuple(self)
, co jest łatwiejsze w utrzymaniu, jeśli w przyszłości do definicji namedtuple zostanie dodanych więcej pól.XYZ = namedtuple("XYZ", "x y z")
działa równie dobrze.namedtuple
s do tego właśnie celu, szczególnie w kodzie matematycznym, w którym funkcja może być wysoce sparametryzowana i mieć kilka współczynników, które mają sens tylko razem.namedtuple
polega na tym, że są one tylko do odczytu. Nie możesz tego zrobićabc.x += 1
ani nic takiego.jawne jest lepsze niż niejawne ... więc upewnij się, że możesz uczynić to bardziej zwięzłym:
Lepsze pytanie brzmi: czy powinieneś?
... to powiedziawszy, że jeśli chcesz nazwaną krotkę, polecam użycie nazwanej krotki (pamiętaj, że krotki mają określone warunki) ... być może chcesz nakazu lub nawet dykta ...
źródło
if k != "self":
można go zmienić naif v is not self:
tani test tożsamości, a nie porównanie ciągów. Wydaje mi się, że technicznie__init__
można by go nazwać drugi raz po konstrukcji i przekazaćself
jako kolejny argument, ale naprawdę nie chcę myśleć, jaki potwór by to zrobił. :-)locals
:set_fields_from_locals(locals())
. Wtedy to nie dłużej niż bardziej magiczne rozwiązania oparte na dekoratorach.Aby rozwinąć
gruszczy
odpowiedź, użyłem wzoru takiego jak:Podoba mi się ta metoda, ponieważ:
super().__init(...)
)X.__init__
W wersjach wcześniejszych niż Python 3.6 nie daje to żadnej kontroli nad kolejnością ustawiania atrybutów, co może być problemem, jeśli niektóre atrybuty są właściwościami z ustawieniami, które mają dostęp do innych atrybutów.
Prawdopodobnie można by to trochę poprawić, ale jestem jedynym użytkownikiem własnego kodu, więc nie martwię się żadną formą oczyszczania danych wejściowych. Być
AttributeError
może bardziej odpowiedni byłby.źródło
Możesz też:
Oczywiście musiałbyś zaimportować
inspect
moduł.źródło
To rozwiązanie bez dodatkowego importu.
Funkcja pomocnicza
Mała funkcja pomocnicza sprawia, że jest wygodniejszy i można go ponownie użyć:
Podanie
Musisz zadzwonić z
locals()
:Test
Wynik:
Bez zmiany
locals()
Jeśli nie chcesz zmieniać,
locals()
użyj tej wersji:źródło
locals()
nie powinny być modyfikowane (może to wpłynąć na interpreter, w twoim przypadku, usunięcieself
z zakresu funkcji wywołującej)self
jest nadal dostępny w__init__
.Ciekawą biblioteką, która to obsługuje (i unika wielu innych schematów), są attrs . Na przykład Twój przykład można sprowadzić do tego (załóżmy, że klasa nazywa się
MyClass
):Nie potrzebujesz
__init__
już nawet metody, chyba że robi ona również inne rzeczy. Oto miłe wprowadzenie Glypha Lefkowitza .źródło
attr
zbędnadataclasses
?Moje 0,02 $. Jest to bardzo zbliżone do odpowiedzi Jorana Beasleya, ale bardziej eleganckie:
Dodatkowo odpowiedź Mike'a Müllera (najlepsza w moim guście) może zostać zredukowana za pomocą tej techniki:
I właśnie telefon
auto_init(locals())
od twojego__init__
źródło
locals()
nie powinny być modyfikowane (niezdefiniowane zachowanie)To naturalny sposób robienia rzeczy w Pythonie. Nie próbuj wymyślać czegoś mądrzejszego, ponieważ doprowadzi to do zbyt sprytnego kodu, którego nikt z Twojego zespołu nie zrozumie. Jeśli chcesz być graczem zespołowym, pisz dalej w ten sposób.
źródło
Python 3.7 i nowsze
W Pythonie 3.7 możesz (nie) używać
dataclass
dekoratora, dostępnego wdataclasses
module. Z dokumentacji:Jeśli klasa jest duża i złożona, to może być niewłaściwe używanie
dataclass
. Piszę to w dniu wydania Pythona 3.7.0, więc wzorce użycia nie są jeszcze dobrze ugruntowane.źródło