Znaczenie @classmethod i @staticmethod dla początkujących?

Czy mógłby mi ktoś wyjaśnić znaczenie @classmethod i @staticmethod w Pythonie? Muszę znać różnicę i znaczenie.

O ile rozumiem, @classmethod mówi klasie, że jest to metoda, która powinna być dziedziczona do podklas, lub... coś. Ale jaki to ma sens? Dlaczego po prostu nie zdefiniować metody klasy bez dodawania @classmethod LUB @staticmethod lub jakichkolwiek definicji @?

Tl; dr: kiedy powinienem ich używać, Dlaczego {[14] } powinienem ich używać i Jak [14]} powinienem ich używać?

Jestem dość zaawansowany w C++, więc używanie bardziej zaawansowanych koncepcji programowania nie powinno być problemem. Proszę podać mi odpowiedni przykład C++, jeśli to możliwe.

Author: martineau, 2012-08-29

11 answers

Chociaż classmethod i staticmethod są dość podobne, istnieje niewielka różnica w użyciu dla obu encji: classmethod musi mieć odniesienie do obiektu klasy jako pierwszego parametru, podczas gdy staticmethod nie może mieć żadnych parametrów w ogóle.

Przykład

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')

Wyjaśnienie

Załóżmy przykład klasy, zajmującej się informacją o dacie (to będzie nasz kocioł):

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

Ta klasa oczywiście może być używana do przechowywania informacji o pewnych datach (bez strefy czasowej informacje; Załóżmy, że wszystkie daty są przedstawione w UTC).

Tutaj mamy __init__, typowy inicjalizator instancji klasy Pythona, który odbiera argumenty jako typowy instancemethod, posiadający pierwszy nieobowiązkowy argument (self), który zawiera odniesienie do nowo utworzonej instancji.

Metoda Klasy

Mamy kilka zadań, które można ładnie wykonać za pomocą classmethod s.

Załóżmy, że chcemy utworzyć wiele instancji klasy Date o dacie informacje pochodzące ze źródła zewnętrznego zakodowane jako ciąg znaków w formacie "dd-mm-RRRR". Załóżmy, że musimy to zrobić w różnych miejscach w kodzie źródłowym naszego projektu.

Więc to, co musimy zrobić, to:

  1. analizuje łańcuch znaków, aby otrzymać dzień, miesiąc i rok jako trzy zmienne całkowite lub krotkę 3-elementową składającą się z tej zmiennej.
  2. Instantiate Date przekazując te wartości do wywołania inicjalizacji.

To będzie wyglądać like:

day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)

W tym celu C++ może zaimplementować taką funkcję z przeciążeniem, ale Python nie ma tego przeciążenia. Zamiast tego możemy użyć classmethod. Stwórzmy kolejny " konstruktor ".

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

date2 = Date.from_string('11-09-2012')

Przyjrzyjmy się dokładniej powyższej implementacji i przyjrzyjmy się jakie mamy tutaj zalety:

  1. zaimplementowaliśmy parsowanie łańcuchów daty w jednym miejscu i teraz Można go ponownie wykorzystać.
  2. enkapsulacja działa tu dobrze (jeśli uważasz, że możesz zaimplementować string parsing jako pojedyncza funkcja gdzie indziej, to rozwiązanie pasuje do paradygmatu OOP znacznie lepiej).
  3. cls jest obiektem, który przechowuje } samą klasę , a nie instancję klasy. To całkiem fajne, ponieważ jeśli odziedziczymy naszą Date klasę, wszystkie dzieci będą miały from_string zdefiniowane również.

Metoda statyczna

A co z staticmethod? Jest dość podobny do classmethod, ale nie przyjmuje żadnych obowiązkowych parametrów (jak metoda klasy lub metoda instancji robi).

Spójrzmy na następny przypadek użycia.

mamy ciąg daty, który chcemy jakoś zweryfikować. To zadanie jest również logicznie powiązane z klasą Date, której używaliśmy do tej pory, ale nie wymaga jej tworzenia instancji.

Tutaj staticmethod może się przydać. Spójrzmy na następny fragment kodu:

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

    # usage:
    is_date = Date.is_date_valid('11-09-2012')

Więc, jak widzimy po użyciu staticmethod, nie mamy dostępu do tego, czym jest klasa- - - to w zasadzie tylko funkcja, zwana składniowo jak metoda, ale bez dostępu do obiektu i jego elementów wewnętrznych( pól i innych metod), podczas gdy classmethod robi.

 2226
Author: Rostyslav Dzinko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-07 06:34:22

Odpowiedź Rostysława Dzinko jest bardzo trafna. Pomyślałem, że mogę wyróżnić jeszcze jeden powód, dla którego powinieneś wybrać @classmethod zamiast @staticmethod podczas tworzenia dodatkowego konstruktora.

W powyższym przykładzie Rostysław użył @classmethod from_string jako fabryka do tworzenia Date obiektów z innych niedopuszczalnych parametrów. To samo można zrobić za pomocą @staticmethod, Jak pokazano w poniższym kodzie:

class Date:
  def __init__(self, month, day, year):
    self.month = month
    self.day   = day
    self.year  = year


  def display(self):
    return "{0}-{1}-{2}".format(self.month, self.day, self.year)


  @staticmethod
  def millenium(month, day):
    return Date(month, day, 2000)

new_year = Date(1, 1, 2013)               # Creates a new Date object
millenium_new_year = Date.millenium(1, 1) # also creates a Date object. 

# Proof:
new_year.display()           # "1-1-2013"
millenium_new_year.display() # "1-1-2000"

isinstance(new_year, Date) # True
isinstance(millenium_new_year, Date) # True

Tak więc zarówno new_year, jak i millenium_new_year są instancjami klasy Date.

Ale jeśli obserwuj uważnie, proces fabryczny jest ciężko zakodowany, aby tworzyć Date obiekty bez względu na wszystko. Oznacza to, że nawet jeśli klasa Date jest podklasowana, podklasy nadal będą tworzyć zwykły obiekt Date (bez żadnej właściwości podklasy). Zobacz to w poniższym przykładzie:

class DateTime(Date):
  def display(self):
      return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year)


datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)

isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # False

datetime1.display() # returns "10-10-1990 - 00:00:00PM"
datetime2.display() # returns "10-10-2000" because it's not a DateTime object but a Date object. Check the implementation of the millenium method on the Date class

datetime2 nie jest instancją DateTime? WTF? To z powodu użytego dekoratora.

W większości przypadków jest to niepożądane. Jeśli to, co chcesz, to metoda Fabryczna, która jest świadoma klasy to się nazywa, więc {[4] } jest tym, czego potrzebujesz. Jest to jedyna część kodu, która się zmienia.]}
@classmethod
def millenium(cls, month, day):
    return cls(month, day, 2000)

cls może być dowolną podklasą. Wynik object będzie słusznie przykładem cls. Sprawdźmy to.

datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)

isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # True


datetime1.display() # "10-10-1990 - 00:00:00PM"
datetime2.display() # "10-10-2000 - 00:00:00PM"

Powodem jest, jak już wiesz, @classmethod został użyty zamiast @staticmethod

 736
Author: Yaw Boakye,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-04 12:19:17

@classmethod znaczy: kiedy ta metoda jest wywoływana, przekazujemy klasę jako pierwszy argument zamiast instancji tej klasy(jak to zwykle robimy z metodami). Oznacza to, że można używać klasy i jej właściwości wewnątrz tej metody, a nie konkretnej instancji.

@staticmethod znaczy: kiedy ta metoda jest wywoływana, nie przekazujemy jej instancji klasy(jak to zwykle robimy z metodami). Oznacza to, że możesz umieścić funkcję wewnątrz klasy, ale nie możesz uzyskać dostępu do instancji tej klasy (jest to przydatne, gdy twoja metoda nie używa instancji).

 215
Author: Simeon Visser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-29 13:40:24

Kiedy używać każdego

@staticmethod funkcja jest niczym innym jak funkcją zdefiniowaną wewnątrz klasy. Można go wywołać bez tworzenia instancji klasy. Jego definicja jest niezmienna poprzez dziedziczenie.

  • Python nie musi tworzyć instancji bound-method dla obiektu.
  • ułatwia czytelność kodu: seeing @ staticmethod , wiemy, że metoda nie zależy od stanu samego obiektu;

@classmethod Funkcja również wywoływalna bez tworzenia instancji klasy, ale jej definicja podąża za podklasą, a nie klasą nadrzędną, poprzez dziedziczenie, może być nadpisana przez podklasę. To dlatego, że pierwszym argumentem dla funkcji @classmethod musi być zawsze cls (class).

  • metody fabryczne , które są używane do tworzenia instancji klasy przy użyciu na przykład pewnego rodzaju wstępnego przetwarzania.
  • metody statyczne wywołanie metod statycznych : jeśli podzielisz metody statyczne na kilka metod statycznych, nie powinieneś kodować na twardo nazwa klasy, ale używaj metod klasy

Tutaj {[32] } jest dobry link do tego tematu.

 55
Author: zangw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-13 09:23:47

Można użyć @classmethod, gdy chce zmienić zachowanie metody, na podstawie której podklasa wywołuje metodę. pamiętaj, że mamy odniesienie do wywołującej klasy w metodzie class.

Podczas używania statycznego, zachowanie powinno pozostać niezmienione w podklasach

Przykład:

class Hero:

  @staticmethod
  def say_hello():
     print("Helllo...")

  @classmethod
  def say_class_hello(cls):
     if(cls.__name__=="HeroSon"):
        print("Hi Kido")
     elif(cls.__name__=="HeroDaughter"):
        print("Hi Princess")

class HeroSon(Hero):
  def say_son_hello(self):
     print("test  hello")



class HeroDaughter(Hero):
  def say_daughter_hello(self):
     print("test  hello daughter")


testson = HeroSon()

testson.say_class_hello() #Output: "Hi Kido"

testson.say_hello() #Outputs: "Helllo..."

testdaughter = HeroDaughter()

testdaughter.say_class_hello() #Outputs: "Hi Princess"

testdaughter.say_hello() #Outputs: "Helllo..."
 31
Author: Krishnendu Kunti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-14 22:52:02

mała kompilacja

@staticmethod Sposób zapisu metody wewnątrz klasy bez odniesienia do obiektu, na którym jest wywoływana. Nie ma więc potrzeby przekazywania ukrytych argumentów, takich jak self czy cls. Jest ona napisana dokładnie tak samo, jak jest napisana poza klasą, ale nie jest bezużyteczna w Pythonie, ponieważ jeśli musisz hermetyzować metodę wewnątrz klasy, ponieważ ta metoda musi być częścią tej klasy @staticmethod jest przydatna w tym case.

@classmethod Jest to ważne, gdy chcesz napisać metodę fabryczną i przez to niestandardowe atrybuty mogą być dołączone do klasy. Ten atrybut(Y) może zostać nadpisany w odziedziczonej klasie.

Porównanie tych dwóch metod może być jak poniżej

Tabela

 28
Author: SIslam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-24 20:50:16

Znaczenie @classmethod i @staticmethod?

    Metoda jest funkcją w przestrzeni nazw obiektu, dostępną jako atrybut.
  • Metoda zwykła (np. instancja) otrzymuje instancję (zwykle nazywamy ją self) jako implicit pierwszy argument.
  • A Klasa metoda pobiera klasę (zwykle nazywamy ją cls) jako domyślny pierwszy argument.
  • A static metoda nie otrzymuje pierwszego argumentu (jak funkcja regularna).

Kiedy powinienem ich używać, dlaczego powinienem ich używać i jak powinienem ich używać?

Nie potrzebujesz żadnego dekoratora. Ale na zasadzie, że należy zminimalizować liczbę argumentów do funkcji( patrz Clean Coder), są one przydatne do tego.
class Example(object):

    def regular_instance_method(self):
        """A function of an instance has access to every attribute of that 
        instance, including its class (and its attributes.)
        Not accepting at least one argument is a TypeError.
        Not understanding the semantics of that argument is a user error.
        """
        return some_function_f(self)

    @classmethod
    def a_class_method(cls):
        """A function of a class has access to every attribute of the class.
        Not accepting at least one argument is a TypeError.
        Not understanding the semantics of that argument is a user error.
        """
        return some_function_g(cls)

    @staticmethod
    def a_static_method():
        """A static method has no information about instances or classes
        unless explicitly given. It just lives in the class (and thus its 
        instances') namespace.
        """
        return some_function_h()

Zarówno dla metod instancji, jak i metod klas, nie akceptowanie co najmniej jednego argumentu jest TypeError, ale nie zrozumienie semantyki tego argumentu jest błąd użytkownika.

(Define some_function ' s,np.:

some_function_h = some_function_g = some_function_f = lambda x=None: x
I to zadziała.)

Przerywane wyszukiwanie instancji i klas:

Przerywane wyszukiwanie na instancji jest wykonywane w tej kolejności-szukamy:

    W przeciwieństwie do innych klas, klasy te nie mogą być używane jako deskryptory.]}
  1. dane w instancji __dict__
  2. nie-deskryptor danych w przestrzeni nazw klas (metody).

Uwaga, przerywane wyszukiwanie na instancji jest wywoływane jak to:

instance = Example()
instance.regular_instance_method 

I metody są atrybutami do wywołania:

instance.regular_instance_method()

Metody instancji

Argument, self, jest niejawnie podawany przez przeszukiwanie kropkowane.

Musisz uzyskać dostęp do metod instancji z instancji klasy.

>>> instance = Example()
>>> instance.regular_instance_method()
<__main__.Example object at 0x00000000399524E0>

Metody klas

Argument, cls, jest niejawnie podawany przez przeszukiwanie kropkowane.

Możesz uzyskać dostęp do tej metody poprzez instancję lub klasę (lub podklasy).

>>> instance.a_class_method()
<class '__main__.Example'>
>>> Example.a_class_method()
<class '__main__.Example'>

Metody statyczne

Nie argumenty są niejawnie podane. Ta metoda działa jak każda funkcja zdefiniowana (na przykład) w przestrzeni nazw modułów, z wyjątkiem tego, że można ją wyszukać

>>> print(instance.a_static_method())
None

PONOWNIE, Kiedy powinienem ich używać, dlaczego powinienem ich używać?

Każda z nich jest coraz bardziej restrykcyjna w informacjach, które przekazują metody w stosunku do instancji.

Używaj ich, gdy nie potrzebujesz informacji.

To sprawia, że funkcje i metody łatwiej rozumować i unittest.

Co jest łatwiejsze do rozumowania?
def function(x, y, z): ...

Lub

def function(y, z): ...

Lub

def function(z): ...

Funkcje z mniejszą ilością argumentów są łatwiejsze do rozumowania. Są również łatwiejsze do zjednoczenia.

Są one podobne do instancji, klas i metod statycznych. Pamiętając o tym, że kiedy mamy instancję, mamy również jej klasę, ponownie zadaj sobie pytanie, co jest łatwiejsze do rozumowania?:
def an_instance_method(self, arg, kwarg=None):
    cls = type(self)             # Also has the class of instance!
    ...

@classmethod
def a_class_method(cls, arg, kwarg=None):
    ...

@staticmethod
def a_static_method(arg, kwarg=None):
    ...

Przykłady budowy

Oto kilka moich ulubionych przykłady wbudowane:

Metoda statyczna str.maketrans była funkcją w module string, ale jest o wiele wygodniej, aby była dostępna z przestrzeni nazw str.

>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'

Metoda klasy dict.fromkeys zwraca nowy słownik utworzony z iterowalnej liczby kluczy:

>>> dict.fromkeys('abc')
{'a': None, 'c': None, 'b': None}

Kiedy podklasowane, widzimy, że pobiera informacje o klasie jako metodę klasy, która jest bardzo przydatna:

>>> class MyDict(dict): pass
>>> type(MyDict.fromkeys('abc'))
<class '__main__.MyDict'> 

Moja rada-podsumowanie

Użyj metod statycznych, gdy nie potrzeba argumentów klasy lub instancji, ale funkcja jest związana z używaniem obiektu i jest wygodne, aby funkcja znajdowała się w przestrzeni nazw obiektu.

Używaj metod klasy, gdy nie potrzebujesz informacji o instancji, ale potrzebujesz informacji o klasie, być może dla innych klas lub metod statycznych, a może dla siebie jako konstruktora. (Nie kodowałbyś na twardo klasy, aby można było tu używać podklas.)

 23
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-21 14:57:43

Jestem początkującym na tej stronie, przeczytałem wszystkie powyższe odpowiedzi i dostałem informacje, czego chcę. Nie mam jednak prawa do głosowania. Chcę zacząć od StackOverflow z odpowiedzią, którą Rozumiem.

  • @staticmethod nie wymaga self lub cls jako pierwszego parametru metody
  • @staticmethod i @classmethod owinięta funkcja może być wywołana przez instancję lub zmienną klasy
  • @staticmethod dekorowana funkcja wpływa na jakąś 'niezmienną właściwość', która podklasa dziedziczenie nie może nadpisać funkcji klasy bazowej, która jest zawinięta przez dekorator @staticmethod.
  • @classmethod potrzebujesz cls (Nazwa klasy, możesz zmienić nazwę zmiennej, jeśli chcesz, ale nie jest to zalecane) jako pierwszy parametr funkcji
  • @classmethod zawsze używane przez sposób podklasy, dziedziczenie podklasy może zmienić efekt funkcji klasy bazowej, tzn. @classmethod owinięta funkcja klasy bazowej może być nadpisana przez różne podklasy.
 1
Author: Yi Chen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-25 13:48:58

Nieco inny sposób myślenia o tym, który może się komuś przydać... Metoda klasowa jest używana w klasie nadrzędnej, aby określić, jak powinna zachowywać się ta metoda, gdy jest wywoływana przez różne klasy potomne. Metoda statyczna jest używana, gdy chcemy zwrócić to samo, niezależnie od klasy potomnej, którą wywołujemy.

 0
Author: boson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-24 22:59:50

W skrócie, @classmehtod zamienia normalną metodę na metodę fabryczną.

Przyjrzyjmy się temu na przykładzie:

class PythonBook:
    def __init__(self, name, author):
        self.name = name
        self.author = author
    def __repr__(self):
        return f'Book: {self.name}, Author: {self.author}'

Bez @ classmethod, powinieneś pracować nad tworzeniem instancji jeden po drugim i są one scartowane.

book1 = PythonBook('Learning Python', 'Mark Lutz')
In [20]: book1
Out[20]: Book: Learning Python, Author: Mark Lutz
book2 = PythonBook('Python Think', 'Allen B Dowey')
In [22]: book2
Out[22]: Book: Python Think, Author: Allen B Dowey

Jak na przykład z @classmethod

class PythonBook:
    def __init__(self, name, author):
        self.name = name
        self.author = author
    def __repr__(self):
        return f'Book: {self.name}, Author: {self.author}'
    @classmethod
    def book1(cls):
        return cls('Learning Python', 'Mark Lutz')
    @classmethod
    def book2(cls):
        return cls('Python Think', 'Allen B Dowey')

Przetestuj to:

In [31]: PythonBook.book1()
Out[31]: Book: Learning Python, Author: Mark Lutz
In [32]: PythonBook.book2()
Out[32]: Book: Python Think, Author: Allen B Dowey
Widzisz? Instancje są z powodzeniem tworzone wewnątrz definicji klasy i są gromadzone razem.

Podsumowując, @ classmethod dekorator konwertuje konwencjonalny metoda do metody fabrycznej, użycie classmethods pozwala dodać tyle alternatywnych konstruktorów, ile jest to konieczne.

 0
Author: JawSaw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-12 11:04:13

Metoda klasy może modyfikować stan klasy, jest powiązana z klasą i zawiera cls jako parametr.

Statyczna metoda nie może modyfikować stanu klasy, jest powiązana z klasą i nie zna klasy ani instancji

class empDetails:
    def __init__(self,name,sal):
        self.name=name
        self.sal=sal
    @classmethod
    def increment(cls,name,none):
        return cls('yarramsetti',6000 + 500)
    @staticmethod
    def salChecking(sal):
        return sal > 6000

emp1=empDetails('durga prasad',6000)
emp2=empDetails.increment('yarramsetti',100)
# output is 'durga prasad'
print emp1.name
# output put is 6000
print emp1.sal
# output is 6500,because it change the sal variable
print emp2.sal
# output is 'yarramsetti' it change the state of name variable
print emp2.name
# output is True, because ,it change the state of sal variable
print empDetails.salChecking(6500)
 -2
Author: y durga prasad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-02 14:49:07