O que significa @classmethod e @staticmethod para principiante?

Alguém me pode explicar o Significado de {[[0]} e @staticmethod em python? Preciso de saber a diferença e o significado.

Tanto quanto sei, diz a uma classe que é um método que deve ser herdado em subclasses, ou... algum. Mas para quê isso? Por que não definir apenas o método da classe sem adicionar @classmethod ou @staticmethod ou quaisquer definições @?

TL; dr: quando devo usá-los, Porque devo usá-los, e Como devo usá-los?

sou bastante avançado com C++, por isso usar conceitos de programação mais avançados não deve ser um problema. Sinta-se à vontade para me dar um exemplo C++ correspondente, se possível.

Author: martineau, 2012-08-29

11 answers

Embora classmethod e staticmethod sejam bastante semelhantes, há uma pequena diferença no uso para ambas as entidades: classmethod deve ter uma referência a um objeto de classe como o primeiro parâmetro, enquanto staticmethod não pode ter parâmetros de todo.

Exemplo

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

Explicação

Vamos assumir um exemplo de uma classe, lidando com a informação da data (esta será a nossa placa):
class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year
Esta classe, obviamente, poderia ser usada para armazenar informações sobre determinadas datas (sem informação do fuso-horário; vamos assumir que todas as datas são apresentadas em UTC).

Aqui temos __init__, um inicializador típico de instâncias de classe Python, que recebe argumentos como um típico instancemethod, tendo o primeiro argumento não-opcional (self) que contém uma referência a uma instância recém-criada.

Método Da Classe

Temos algumas tarefas que podem ser bem feitas usando classmethods.

vamos supor que queremos criar um monte de instâncias de classe Data informação proveniente de uma fonte exterior codificada como uma cadeia de caracteres com o formato "dd-mm-AAAA". Suponha que temos que fazer isso em diferentes lugares no código fonte de nosso projeto.

Então o que temos de fazer aqui é:
  1. processar uma string para receber dia, mês e ano como três variáveis inteiras ou uma tupla de 3 itens que consiste nessa variável.
  2. instanciar {[13] } passando esses valores para a chamada de inicialização.
Isto vai parecer ... tipo:
day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)

Para este efeito, o C++ pode implementar essa funcionalidade com sobrecarga, mas o Python não tem esta sobrecarga. Em vez disso, podemos usar classmethod. Vamos criar outro construtor.

    @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')
Vamos olhar mais atentamente para a implementação acima, e rever quais as vantagens que temos aqui:
    Implementámos o processamento de string de datas num só lugar e agora é reutilizável. Encapsulamento funciona bem aqui (se você acha que pode implementar string analisando como uma única função em outro lugar, esta solução se encaixa muito melhor no paradigma OOP).
  1. cls é um objeto que possui a própria classe, Não uma instância da classe. É muito fixe, porque se herdarmos a nossa classe, todas as crianças terão definido também.

Método Estático

E quanto a...? É bastante semelhante a classmethod mas não toma quaisquer parâmetros obrigatórios (como um método de classe ou método de instância o). Vamos ver o próximo caso de uso.

temos uma data que queremos validar de alguma forma. Esta tarefa também está logicamente ligada à classe Date que usamos até agora, mas não requer instanciação dela.

Aqui é onde staticmethod pode ser útil. Vamos ver o próximo pedaço de código:
    @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')
Então, como podemos ver pelo uso de staticmethod, não temos qualquer acesso ao que a classe é - - - é basicamente apenas uma função, chamada sintaticamente como um método, mas sem acesso ao objeto e seus internos (campos e outros métodos), enquanto classmetod o faz.
 2226
Author: Rostyslav Dzinko, 2018-09-07 06:34:22
A resposta de Rostyslav Dzinko é muito apropriada. Eu pensei que poderia destacar uma outra razão pela qual você deve escolher @classmethod sobre @staticmethod quando você está criando um construtor adicional. No exemplo acima, Rostyslav utilizou o @classmethod from_string como uma fábrica para criar objetos Date a partir de outros parâmetros inaceitáveis. O mesmo pode ser feito com @staticmethod Como é mostrado no código abaixo:
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

Assim, tanto new_year como millenium_new_year são instâncias da classe Date.

Mas, se observe atentamente, o processo de fábrica é difícil de codificar para criar objetos Date não importa o que aconteça. O que isso significa é que mesmo que a classe Date seja subclassificada, as subclasses ainda criarão um objeto simples Date (sem qualquer propriedade da subclasse). Veja no exemplo abaixo:

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 não é um exemplo de DateTime? WTF? Bem, isso é por causa do decorador usado.

Na maioria dos casos, isto é indesejado. Se o que você quer é um método de fábrica que está ciente da classe isso chamou-lhe, então @classmethod é o que você precisa.

Reescrever o Date.millenium como (essa é a única parte do código acima que muda)

@classmethod
def millenium(cls, month, day):
    return cls(month, day, 2000)

Assegura que o class não é codificado, mas sim aprendido. cls pode ser qualquer subclasse. O resultado object será justamente um exemplo de cls. Vamos testar isso.

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"

A razão é, como você sabe agora, @classmethod foi usado em vez de @staticmethod

 736
Author: Yaw Boakye, 2017-09-04 12:19:17

@classmethod significa: quando este método é chamado, passamos a classe como o primeiro argumento em vez de a instância dessa classe (como normalmente fazemos com métodos). Isto significa que você pode usar a classe e suas propriedades dentro desse método em vez de uma instância particular.

@staticmethod significa: quando este método é chamado, Nós não passamos uma instância da classe para ele (como normalmente fazemos com métodos). Isso significa que você pode colocar uma função dentro de uma classe, mas você não pode acessar a instância de que classe (isto é útil quando o seu método não usa a instância).

 215
Author: Simeon Visser, 2012-08-29 13:40:24

Quando utilizar cada

@staticmethod função não é nada mais do que uma função definida dentro de uma classe. É chamável sem instanciar a classe em primeiro lugar. A sua definição é imutável através da herança.

  • o Python não tem de instanciar um método de encadernação para um objecto.
  • facilita a legibilidade do Código: ver @staticmetod , sabemos que o método não depende do Estado do próprio objecto;

@classmethod função também callable without instantiatiating the class, but its definition follows Sub class, not Parent class, via inheritance, can be overridden by subclass. Isso porque o primeiro argumento para a função @classmethod deve ser sempre cls (class).

  • Métodos de fábrica , que são usados para criar uma instância para uma classe usando, por exemplo, algum tipo de pré-processamento.
  • métodos estáticos chamando métodos estáticos : Se você dividir um método estático em vários métodos estáticos, você não deve codificar o nome da classe, mas usar métodos da classe

Aqui é um bom link para este tópico.

 55
Author: zangw, 2018-09-13 09:23:47

Usaria-se @classmethod quando quisesse mudar o comportamento do método baseado no qual a subclasse chama o método. lembre-se que temos uma referência à classe chamadora em um método de classe.

Ao usar estática, gostaria que o comportamento permanecesse inalterado nas subclasses

Exemplo:

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, 2017-06-14 22:52:02

uma pequena compilação

@método estático Uma maneira de escrever um método dentro de uma classe sem referência ao objeto que está sendo chamado. Então, não há necessidade de passar argumentos implícitos como self ou cls. Ele é escrito exatamente da mesma forma que escrito fora da classe, mas não é de nenhum uso em python porque se você precisa encapsular um método dentro de uma classe uma vez que este método precisa ser a parte dessa classe @staticmethod é útil nisso caso.

@classmetod É importante quando você quer escrever um método de fábrica e por este atributo personalizado pode ser anexado em uma classe. Este(s) atributo (s) pode ser sobreposto na classe herdada.

Uma comparação entre estes dois métodos pode ser a seguinte

Table

 28
Author: SIslam, 2015-07-24 20:50:16

O que significa @classmethod e @staticmethod?

  • um método é uma função no espaço de nomes de um objeto, acessível como um atributo.
  • um método regular (ou seja, instância) obtém a instância (normalmente chamamos-lhe self) como o primeiro argumento implícito.
  • A classe o método obtém a classe (normalmente o chamamos de cls) como o primeiro argumento implícito.
  • A estática o método não obtém nenhum primeiro argumento implícito (como um função regular).

Quando devo usá-los, por que devo usá-los, e como devo usá-los?

Não precisas de decorador nenhum. Mas com o princípio de que você deve minimizar o número de argumentos para funções (veja Coder limpo), eles são úteis para fazer exatamente isso.
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()

Para métodos de instância e métodos de classe, não aceitar pelo menos um argumento é um erro tipográfico, mas não entender a semântica desse argumento é um erro do utilizador.

(definir some_function's, por exemplo:

some_function_h = some_function_g = some_function_f = lambda x=None: x
E isto vai funcionar.)

Pesquisas pontilhadas em instâncias e classes:

Uma pesquisa pontilhada sobre uma instância é realizada nesta ordem-procuramos por:

  1. um descritor de dados no espaço de nomes da classe (como uma propriedade)
  2. dados no caso __dict__
  3. um descritor de não-dados no espaço de nomes da classe (métodos).
Nota, uma pesquisa pontilhada numa instância é invocada como isto:
instance = Example()
instance.regular_instance_method 

E os métodos são atributos chamáveis:

instance.regular_instance_method()

Métodos de instância

O argumento, self, é implicitamente dado através da pesquisa pontilhada.

Você deve acessar os métodos de instância a partir de instâncias da classe.

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

Métodos de classe

O argumento, cls, é implicitamente dado através de pesquisa pontilhada.

Pode aceder a este método através de uma instância ou da classe (ou subclasses).

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

Métodos estáticos

Não argumentos são dados implicitamente. Este método funciona como qualquer função definida (por exemplo) num espaço de nomes de módulos, excepto que pode ser pesquisada

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

Novamente, quando devo usá-los, por que devo usá-los?

Cada uma destas são progressivamente mais restritivas na informação que passam pelo método versus os métodos de instância. Use-os quando não precisar da informação.

Isto torna as suas funções e métodos mais fáceis de raciocinar sobre e para unidade.

Qual é a razão mais fácil?
def function(x, y, z): ...

Ou

def function(y, z): ...

Ou

def function(z): ...

As funções com menos argumentos são mais fáceis de raciocinar. Eles também são mais fáceis de se unificar.

Isto é semelhante a exemplo, classe e métodos estáticos. Tendo em mente que quando temos uma instância, também temos sua classe, novamente, pergunte a si mesmo, o que é mais fácil de raciocinar?:

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):
    ...

Exemplos de construção

Aqui estão algumas das minhas favoritas. exemplos de compilação:

O método estático str.maketrans era uma função no módulo string, mas é muito mais conveniente para ele ser acessível a partir do espaço de nomes str.

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

O método da classe dict.fromkeys devolve um novo dicionário instanciado a partir de um iterável de chaves:

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

Quando subclassificado, vemos que obtém a informação da classe como um método da classe, o que é muito útil:

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

O meu conselho-conclusão

Usa métodos estáticos quando não o fizeres. precisa dos argumentos de classe ou instância, mas a função está relacionada com o uso do objeto, e é conveniente para a função estar no espaço de nomes do objeto.

Use métodos de classe quando você não precisa de informação de instância, mas precisa da informação de classe talvez para sua outra classe ou métodos estáticos, ou talvez ele mesmo como um construtor. (Você não codificaria a classe para que as subclasses pudessem ser usadas aqui.)

 23
Author: Aaron Hall, 2018-03-21 14:57:43
Sou um principiante neste site, li todas as respostas acima e obtive a informação que quero. No entanto, não tenho o direito de votar. Então eu quero começar com StackOverflow com a resposta que eu entendo.
  • @staticmethod não precisa de si próprio ou cls como primeiro parâmetro do método
  • @staticmethod e @classmethod a função repartida pode ser chamada por instância ou variável de classe
  • A função decorada impacta uma espécie de "propriedade imutável" que a herança de subclasses não pode sobrepor a sua função de classe base que é embrulhada por um decorador @staticmethod.
  • @classmethod necessita de cls (nome da classe, pode alterar o nome da variável se quiser, mas não é aconselhável) como primeiro parâmetro da função
  • {[2] } sempre usado pela subclasse manner, a herança da subclasse pode alterar o efeito da função da classe base, ou seja @classmethod a função da classe base embrulhada pode ser substituída por diferentes subclasses.
 1
Author: Yi Chen, 2017-07-25 13:48:58
Uma forma ligeiramente diferente de pensar que possa ser útil para alguém... Um método de classe é usado em uma superclasse para definir como esse método deve se comportar quando é chamado por classes de crianças diferentes. Um método estático é usado quando queremos retornar a mesma coisa independentemente da classe de crianças que estamos chamando.
 0
Author: boson, 2017-01-24 22:59:50
Em resumo, @classmehtod transforma um método normal num método de fábrica. Vamos explorá-lo com um exemplo:
class PythonBook:
    def __init__(self, name, author):
        self.name = name
        self.author = author
    def __repr__(self):
        return f'Book: {self.name}, Author: {self.author}'

Sem um @classmethod, você deve trabalhar para criar instâncias um a um e eles são scartted.

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

Como por exemplo com @classmetod

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

Testa-o:

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
Vês? Instâncias são criadas com sucesso dentro de uma definição de classe e elas são coletadas juntas.

Em conclusão, @classmetod decorator converter a o método convencional a um método de fábrica, usando métodos de classe, torna possível adicionar tantos Construtores alternativos quanto necessário.

 0
Author: JawSaw, 2017-12-12 11:04:13

O método da classe pode modificar o estado da classe,ligando-se à classe e contém cls como parâmetro.

O método estático não pode modificar o estado da classe, liga-se à classe e não conhece a classe ou instância

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, 2017-09-02 14:49:07