Uczę się Pythona, a moją ostatnią lekcją było to, że Python nie jest Javą , więc spędziłem trochę czasu przekształcając wszystkie moje metody klasy w funkcje.
Teraz zdaję sobie sprawę, że nie muszę używać metod Class do tego, co zrobiłbym z static
metodami w Javie, ale teraz nie jestem pewien, kiedy ich użyję. Wszystkie porady, które mogę znaleźć na temat metod klasy Python, są zgodne z zasadami dla początkujących, takich jak ja, powinni unikać ich, a standardowa dokumentacja jest najbardziej nieprzejrzysta podczas ich omawiania.
Czy ktoś ma dobry przykład użycia metody klasy w Pythonie, a przynajmniej ktoś może mi powiedzieć, kiedy metody klasy można rozsądnie zastosować?
źródło
obj.cls_mthd(...)
lubtype(obj).cls_mthd(...)
?Metody fabryczne (konstruktory alternatywne) są rzeczywiście klasycznym przykładem metod klasowych.
Zasadniczo metody klasowe są odpowiednie zawsze, gdy chcesz mieć metodę, która naturalnie pasuje do przestrzeni nazw klasy, ale nie jest powiązana z konkretnym wystąpieniem klasy.
Na przykład w doskonałym module unipath :
Aktualny katalog
Path.cwd()
Path("/tmp/my_temp_dir")
. To jest metoda klasowa..chdir()
Ponieważ bieżący katalog obejmuje cały proces,
cwd
metoda nie ma konkretnej instancji, z którą należy ją powiązać. Jednak zmianacwd
katalogu na danąPath
instancję powinna rzeczywiście być metodą instancji.Hmmm ... jak
Path.cwd()
zresztą zwracaPath
instancję, myślę, że można ją uznać za metodę fabryczną ...źródło
Pomyśl o tym w ten sposób: normalne metody są przydatne, aby ukryć szczegóły wysyłki: możesz pisać
myobj.foo()
bez martwienia się, czyfoo()
metoda jest zaimplementowana przezmyobj
klasę obiektu, czy jedną z jego klas nadrzędnych. Metody klasy są dokładnie analogiczne do tego, ale zamiast tego z obiektem klasy: pozwalają na wywołanieMyClass.foo()
bez martwienia się ofoo()
to, czy jest implementowane specjalnie przezMyClass
to, że potrzebował własnej specjalizowanej wersji, czy też pozwala klasie macierzystej obsłużyć wywołanie.Metody klasowe są niezbędne, gdy wykonujesz konfigurację lub obliczenia, które poprzedzają utworzenie rzeczywistej instancji, ponieważ dopóki instancja nie istnieje, oczywiście nie możesz użyć instancji jako punktu wysyłki dla wywołań metod. Dobry przykład można zobaczyć w kodzie źródłowym SQLAlchemy; spójrz na
dbapi()
metodę klasy pod następującym linkiem:https://github.com/zzzeek/sqlalchemy/blob/ab6946769742602e40fb9ed9dde5f642885d1906/lib/sqlalchemy/dialects/mssql/pymssql.py#L47
Widać, że
dbapi()
metoda, której backend bazy danych używa do importowania biblioteki bazy danych specyficznej dla dostawcy, której potrzebuje na żądanie, jest metodą klasową, ponieważ musi zostać uruchomiona, zanim zaczną się tworzyć instancje określonego połączenia z bazą danych - ale nie może być prostą funkcją lub funkcją statyczną, ponieważ chcą, aby mogła wywoływać inne, wspierające metody, które podobnie mogą wymagać napisania dokładniej w podklasach niż w klasie nadrzędnej. A jeśli wysyłasz do funkcji lub klasy statycznej, wówczas „zapominasz” i tracisz wiedzę o tym, która klasa inicjuje.źródło
Ostatnio chciałem mieć bardzo lekką klasę rejestrowania, która generowałaby różne ilości danych wyjściowych w zależności od poziomu rejestrowania, który można ustawić programowo. Ale nie chciałem tworzyć instancji klasy za każdym razem, gdy chciałem wyświetlać komunikat debugowania, błąd lub ostrzeżenie. Ale chciałem także podsumować funkcjonowanie tego narzędzia do rejestrowania i umożliwić jego ponowne użycie bez deklaracji globali.
Użyłem więc zmiennych klas i
@classmethod
dekoratora, aby to osiągnąć.Dzięki mojej prostej klasie Logging mogę wykonać następujące czynności:
Następnie, w moim kodzie, jeśli chciałem wyrzucić kilka informacji debugujących, musiałem po prostu napisać kod
Błędy można wyeliminować
W środowisku „produkcyjnym” mogę określić
a teraz zostanie wyświetlony tylko komunikat o błędzie. Komunikat debugowania nie zostanie wydrukowany.
Oto moja klasa:
I trochę kodu, który to trochę testuje:
źródło
Alternatywnymi konstruktorami są klasyczny przykład.
źródło
Gdy użytkownik loguje się na mojej stronie, obiekt User () jest tworzony na podstawie nazwy użytkownika i hasła.
Jeśli potrzebuję obiektu użytkownika bez zalogowanego użytkownika (np. Administrator może chcieć usunąć konto innego użytkownika, więc muszę utworzyć instancję tego użytkownika i wywołać jego metodę usuwania):
Mam metody klasowe do przechwycenia obiektu użytkownika.
źródło
Myślę, że najbardziej jednoznaczna jest odpowiedź AmanKow . Sprowadza się to do tego, jak chcesz uporządkować swój kod. Możesz napisać wszystko jako funkcje na poziomie modułu, które są opakowane w przestrzeń nazw modułu tj
Powyższy kod proceduralny nie jest dobrze zorganizowany, ponieważ po zaledwie 3 modułach robi się mylący, co robi każda metoda? Możesz używać długich opisowych nazw dla funkcji (jak w Javie), ale nadal twój kod staje się niemożliwy do zarządzania bardzo szybko.
Zorientowanym obiektowo sposobem jest rozbicie kodu na zarządzalne bloki, tzn. Klasy i obiekty oraz funkcje mogą być powiązane z instancjami obiektów lub klasami.
Dzięki funkcjom klasowym zyskujesz inny poziom podziału w kodzie w porównaniu z funkcjami na poziomie modułu. Możesz więc pogrupować powiązane funkcje w obrębie klasy, aby uczynić je bardziej specyficznymi dla zadania przypisanego do tej klasy. Na przykład możesz utworzyć klasę plików:
Ten sposób jest bardziej elastyczny i łatwiejszy w utrzymaniu, grupujesz funkcje razem i jest to bardziej oczywiste dla tego, co robi każda funkcja. Zapobiegasz również konfliktom nazw, na przykład kopia funkcji może istnieć w innym zaimportowanym module (na przykład kopii sieciowej), którego używasz w kodzie, więc używając pełnej nazwy FileUtil.copy () usuwasz problem i obie funkcje kopiowania można stosować obok siebie.
źródło
FileUtil
metody klas jako funkcjefile_util
modułu, byłoby to porównywalne i nie nadużywałoby OOP, gdy w rzeczywistości nie istniały żadne obiekty (w rzeczywistości można argumentować, że jest to preferowane, ponieważ nie kończy się tofrom file_util import FileUtil
ani z innymi gadatliwościami). Konflikty nazw można w podobny sposób uniknąć w kodzie proceduralnym, wykonującimport file_util
zamiastfrom file_util import ...
.Szczerze? Nigdy nie znalazłem zastosowania metody statycznej ani metody klasowej. Nie widziałem jeszcze operacji, której nie można wykonać za pomocą funkcji globalnej lub metody instancji.
Byłoby inaczej, gdyby Python używał prywatnych i chronionych członków bardziej niż Java. W Javie potrzebuję metody statycznej, aby móc uzyskać dostęp do prywatnych członków instancji w celu wykonywania różnych czynności. W Pythonie rzadko jest to konieczne.
Zwykle widzę ludzi używających metod statycznych i metod klasy, gdy wszystko, co naprawdę muszą zrobić, to lepiej używać przestrzeni nazw na poziomie modułu w Pythonie.
źródło
Pozwala pisać ogólne metody klas, których można używać z dowolną kompatybilną klasą.
Na przykład:
Jeśli nie używasz
@classmethod
, możesz to zrobić za pomocą własnego słowa kluczowego, ale wymaga ono wystąpienia klasy:źródło
Pracowałem z PHP, a ostatnio zastanawiałem się, co się dzieje z tą klasą? Podręcznik Pythona jest bardzo techniczny i bardzo krótki, więc nie pomoże w zrozumieniu tej funkcji. Byłem googling i googling i znalazłem odpowiedź -> http://code.anjanesh.net/2007/12/python-classmethods.html .
Jeśli jesteś leniwy, kliknij go. Moje wyjaśnienie jest krótsze i poniżej. :)
w PHP (może nie wszyscy znacie PHP, ale ten język jest tak prosty, że wszyscy powinni zrozumieć, o czym mówię) mamy zmienne statyczne takie jak to:
Wynik będzie w obu przypadkach 20.
Jednak w Pythonie możemy dodać dekorator @classmethod, a zatem możliwe jest uzyskanie odpowiednio 10 i 20. Przykład:
Sprytne, prawda?
źródło
B.setInnerVar(20)
został pominięty, wydrukowałby10
dwa razy (zamiast dawać błąd przy drugim wywołaniu echoInnerBar (), który nieinner_var
został zdefiniowanyMetody klasowe zapewniają „cukier semantyczny” (nie wiem, czy termin ten jest powszechnie używany) - lub „wygodę semantyczną”.
Przykład: masz zestaw klas reprezentujących obiekty. Możesz chcieć mieć metodę klasy
all()
lubfind()
pisaćUser.all()
lubUser.find(firstname='Guido')
. Można to oczywiście zrobić za pomocą funkcji na poziomie modułu ...źródło
To, co właśnie mnie uderzyło, wywodzące się z Ruby, to fakt, że tak zwana metoda klasowa i tak zwana metoda instancji jest tylko funkcją o znaczeniu semantycznym zastosowaną do jej pierwszego parametru, który jest cicho przekazywany, gdy funkcja jest wywoływana jako metoda obiekt (tj
obj.meth()
).Zwykle ten obiekt musi być instancją, ale
@classmethod
dekorator metod zmienia reguły przekazywania klasy. Możesz wywołać metodę klasy na instancji (to tylko funkcja) - pierwszym argumentem będzie jej klasa.Ponieważ jest to tylko funkcja , można ją zadeklarować tylko raz w dowolnym zakresie (tj.
class
Definicji). Dlatego też, jako niespodzianka dla Rubyist, że nie możesz mieć metody klasy i metody instancji o tej samej nazwie .Rozważ to:
Możesz zadzwonić
foo
do instancjiAle nie na zajęciach:
Teraz dodaj
@classmethod
:Wywołanie instancji przechodzi teraz przez jej klasę:
podobnie jak wzywanie klasy:
Jest to tylko konwencja, która nakazuje, abyśmy używali
self
tego pierwszego argumentu w metodzie instancji icls
metodzie klasy. Nie użyłem żadnego z nich, aby zilustrować, że to tylko argument. W Rubyself
jest słowem kluczowym.Porównaj z Ruby:
Metoda klasy Python jest tylko dekorowaną funkcją i możesz używać tych samych technik do tworzenia własnych dekoratorów . Udekorowana metoda otacza prawdziwą metodę (w przypadku,
@classmethod
gdy przekazuje dodatkowy argument klasy). Podstawowa metoda jest nadal dostępna, ukryta, ale nadal dostępna .przypis: Napisałem to po konflikcie nazw między metodą klasy a instancji, która wzbudziła moją ciekawość. Jestem daleki od eksperta w Pythonie i chciałbym komentować, jeśli którekolwiek z nich jest złe.
źródło
super()
). AFICT, „magia” ma miejsce, gdy atrybut jest ustawiony lub odczytany. Wywołanieobj.meth(arg)
w Pythonie (w przeciwieństwie do Ruby) oznacza po prostu(obj.meth)(arg)
. Nigdzie nic nie jest po cichu przekazywane,obj.meth
to tylko obiekt na żądanie, który akceptuje jeden argument mniej niż funkcja, z której został utworzony.obj.meth
z kolei oznacza po prostugetattr(obj, "meth")
.To ciekawy temat. Uważam, że metoda klasy python działa jak singleton, a nie fabryka (która zwraca wyprodukowaną instancję klasy). Jest to singleton, ponieważ istnieje wspólny obiekt, który jest generowany (słownik), ale tylko raz dla klasy, ale wspólny dla wszystkich instancji.
Aby to zilustrować, oto przykład. Zauważ, że wszystkie instancje mają odniesienie do pojedynczego słownika. To nie jest wzór fabryczny, jak go rozumiem. Jest to prawdopodobnie bardzo unikalne dla Pythona.
źródło
Kilka razy zadawałem sobie to samo pytanie. I chociaż chłopaki tutaj starali się to wytłumaczyć, IMHO najlepszą odpowiedzią (i najprostszą), jaką znalazłem, jest opis metody Class w Dokumentacji Pythona.
Istnieje również odniesienie do metody statycznej. A jeśli ktoś już zna metody instancji (które, jak zakładam), ta odpowiedź może być ostatnim elementem, który poskłada wszystko razem ...
Dalsze i głębsze opracowanie na ten temat można znaleźć również w dokumentacji: Standardowa hierarchia typów (przewiń w dół do sekcji Metody wystąpienia )
źródło
@classmethod
może być przydatny do łatwego tworzenia instancji obiektów tej klasy z zasobów zewnętrznych. Rozważ następujące:Następnie w innym pliku:
Dostęp do inst.x da tę samą wartość co ustawienia ['x'].
źródło
Klasa oczywiście określa zestaw instancji. Metody klasy działają na poszczególnych instancjach. Metody klasy (i zmienne) to miejsce do zawieszenia wszystkich informacji związanych z zestawem instancji.
Na przykład, jeśli twoja klasa definiuje zestaw uczniów, możesz chcieć mieć zmienne klasowe lub metody, które definiują takie rzeczy, jak zestaw ocen, do których uczniowie mogą należeć.
Możesz także użyć metod klasowych do zdefiniowania narzędzi do pracy nad całym zestawem. Na przykład Student.all_of_em () może zwrócić wszystkich znanych studentów. Oczywiście, jeśli twój zestaw instancji ma więcej struktur niż tylko zestaw, możesz podać metody klasowe, które będą wiedzieć o tej strukturze. Students.all_of_em (grade = „juniorzy”)
Takie techniki prowadzą do przechowywania elementów zbioru instancji w strukturach danych zakorzenionych w zmiennych klasowych. Musisz wtedy uważać, aby nie frustrować zbierania śmieci.
źródło