cls - staticmethod() python




Qual é a diferença entre @staticmethod e @classmethod? (16)

Qual é a diferença entre uma função decorada com @staticmethod e outra decorada com @classmethod ?


Qual é a diferença entre @staticmethod e @classmethod no Python?

Você pode ter visto código Python como este pseudocódigo, que demonstra as assinaturas dos vários tipos de métodos e fornece uma docstring para explicar cada um:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

O método de instância normal

Primeiro eu explicarei a_normal_instance_method . Isso é precisamente chamado de " método de instância ". Quando um método de instância é usado, ele é usado como uma função parcial (em oposição a uma função total, definida para todos os valores quando visualizados no código-fonte), ou seja, quando usado, o primeiro dos argumentos é predefinido como a instância do objeto, com todos os seus atributos dados. Ele tem a instância do objeto ligado a ele e deve ser chamado a partir de uma instância do objeto. Normalmente, ele acessará vários atributos da instância.

Por exemplo, esta é uma instância de uma string:

', '

se usarmos o método da instância, join nessa cadeia, para unir a outra iterável, obviamente é uma função da instância, além de ser uma função da lista iterável, ['a', 'b', 'c'] :

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

Métodos encadernados

Os métodos de instância podem ser vinculados por meio de uma pesquisa pontilhada para uso posterior.

Por exemplo, isso liga o método str.join à instância ':' :

>>> join_with_colons = ':'.join 

E depois podemos usar isso como uma função que já possui o primeiro argumento vinculado a ele. Desta forma, funciona como uma função parcial na instância:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

Método Estático

O método estático não considera a instância como um argumento.

É muito semelhante a uma função de nível de módulo.

No entanto, uma função de nível de módulo deve residir no módulo e ser especialmente importada para outros locais onde é usada.

Se estiver anexado ao objeto, no entanto, ele seguirá o objeto convenientemente através da importação e herança também.

Um exemplo de um método estático é str.maketrans , movido do módulo de string no Python 3. Ele faz uma tabela de conversão adequada para consumo por str.translate . Parece um pouco bobo quando usado a partir de uma instância de uma string, como demonstrado abaixo, mas importar a função do módulo string é um pouco desajeitado, e é bom poder chamá-la da classe, como em str.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

No python 2, você tem que importar esta função do módulo de string cada vez menos útil:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

Método de Classe

Um método de classe é semelhante a um método de instância, pois usa um primeiro argumento implícito, mas, em vez de usar a instância, ele usa a classe. Freqüentemente eles são usados ​​como construtores alternativos para melhor uso semântico e suportarão herança.

O exemplo mais canônico de um dict.fromkeys é o dict.fromkeys . Ele é usado como um construtor alternativo do dict, (adequado para quando você sabe quais são suas chaves e deseja um valor padrão para elas.)

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

Quando subclassificamos dict, podemos usar o mesmo construtor, que cria uma instância da subclasse.

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

Veja o código fonte do pandas para outros exemplos similares de construtores alternativos, e veja também a documentação oficial do Python no classmethod e staticmethod .


@classmethod: pode ser usado para criar um acesso global compartilhado a todas as instâncias criadas dessa classe ..... como atualizar um registro de vários usuários ... Eu particularmente achei que ele usaria toda a criação de singletons também ...: )

Método @static: não tem nada a ver com a classe ou instância sendo associada com ... mas para legibilidade pode usar o método estático


Basicamente, @classmethod faz um método cujo primeiro argumento é a classe da qual é chamado (em vez da instância da classe), @staticmethod não possui nenhum argumento implícito.


Deixe-me dizer a semelhança entre um método decorado com @classmethod vs @staticmethod primeiro.

Semelhança: Ambos podem ser chamados na própria Classe , em vez de apenas na instância da classe. Então, ambos em certo sentido são métodos da classe .

Diferença: Um classmethod receberá a própria classe como o primeiro argumento, enquanto um staticmethod não.

Portanto, um método estático, em certo sentido, não está vinculado à própria Classe e está pendurado nela apenas porque pode ter uma funcionalidade relacionada.

class C(object):
    @classmethod
    def fun(cls, arg1, arg2, ...):
       ....

fun: function that needs to be converted into a class method
returns: a class method for function.

Eu comecei a aprender linguagem de programação com C ++ e depois com Java e Python, e então essa questão me incomodou muito também, até que eu entendi o uso simples de cada um.

Método de classe: Python, ao contrário de Java e C ++, não tem sobrecarga de construtor. E assim, para conseguir isso, você poderia usar o classmethod . O exemplo a seguir explicará isso

Vamos considerar que temos uma classe Person que recebe dois argumentos first_name e last_name e cria a instância de Person.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Agora, se o requisito chegar onde você precisa criar uma classe usando apenas um único nome, apenas um first_name , você não pode fazer algo assim em python.

Isso lhe dará um erro quando você tentar criar um objeto (instância).

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __init__(self, first_name):
        self.first_name = first_name

No entanto, você pode conseguir a mesma coisa usando @classmethod como mencionado abaixo

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

Método estático :: Isto é bastante simples, não está vinculado a instância ou classe e você pode simplesmente chamar isso usando o nome da classe.

Então, digamos que no exemplo acima você precisa de uma validação que first_name não deve exceder 20 caracteres, você pode simplesmente fazer isso.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

e você poderia simplesmente chamar usando o nome da classe

Person.validate_name("Gaurang Shah")

Minha contribuição demonstra a diferença entre os @classmethod , @staticmethod e instance, incluindo como uma instância pode chamar indiretamente um @staticmethod .Mas, em vez de chamar indiretamente um @staticmethodde uma instância, torná-lo privado pode ser mais "pythônico". Obter algo de um método privado não é demonstrado aqui, mas é basicamente o mesmo conceito.

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

Para decidir se deve usar @staticmethod ou @classmethod você precisa examinar seu método. Se o seu método acessar outras variáveis ​​/ métodos em sua classe, use @classmethod . Por outro lado, se o seu método não tocar em nenhuma outra parte da classe, use @staticmethod.

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to other class,
        #       you don't have to rename the class reference 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Make juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing %d...' % apple)
        cls._counter += 1

Talvez um pouco de código de exemplo ajude: Observe a diferença nas assinaturas de chamadas de foo , class_foo e static_foo :

class A(object):
    def foo(self,x):
        print "executing foo(%s,%s)"%(self,x)

    @classmethod
    def class_foo(cls,x):
        print "executing class_foo(%s,%s)"%(cls,x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)"%x    

a=A()

Abaixo está a maneira usual como uma instância de objeto chama um método. A instância do objeto, a , é implicitamente passada como o primeiro argumento.

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

Com classmethods , a classe da instância do objeto é implicitamente passada como primeiro argumento em vez de self .

a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Você também pode chamar class_foo usando a classe. De fato, se você definir algo para ser um método de classe, provavelmente é porque você pretende chamá-lo da classe e não de uma instância de classe. A.foo(1) teria levantado um TypeError, mas A.class_foo(1) funciona muito bem:

A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Um uso que as pessoas encontraram para os métodos de classe é criar construtores alternativos hereditários .

Com staticmethods , nem self (a instância do objeto) nem cls (a classe) são implicitamente passados ​​como o primeiro argumento. Eles se comportam como funções simples, exceto que você pode chamá-los de uma instância ou da classe:

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

Os métodos estáticos são usados ​​para agrupar funções que possuem alguma conexão lógica com uma classe para a classe.

foo é apenas uma função, mas quando você chama a.foo você não apenas obtém a função, você obtém uma versão "parcialmente aplicada" da função com a instância do objeto a limite como o primeiro argumento para a função. foo espera 2 argumentos, enquanto a.foo espera apenas 1 argumento.

a é obrigado a foo . É isso que significa o termo "ligado" abaixo:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

Com a.class_foo , a não está vinculada a class_foo , mas a classe A está vinculada a class_foo .

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

Aqui, com um método a.static_foo , mesmo que seja um método, a.static_foo apenas retorna uma função ole boa sem argumentos ligados. static_foo espera 1 argumento e a.static_foo espera 1 argumento também.

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

E, claro, a mesma coisa acontece quando você chama static_foo com a classe A

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

Vou tentar explicar a diferença básica usando um exemplo.

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - podemos chamar diretamente métodos estáticos e de classe sem inicializar

# A.run_self() #  wrong
A.run_static()
A.run_class()

2- O método estático não pode chamar o método self mas pode chamar outro método estático e de classe

3- O método estático pertence à classe e não usará objeto algum.

4- O método de classe não está ligado a um objeto, mas a uma classe.


Here está um pequeno artigo sobre esta questão

A função @staticmethod não é nada mais que uma função definida dentro de uma classe. É chamado sem instanciar a classe primeiro. Sua definição é imutável via herança.

A função @classmethod também pode ser chamada sem instanciar a classe, mas sua definição segue Subclasse, não Parent class, via inheritance. Isso porque o primeiro argumento para a função @classmethod sempre deve ser cls (class).


@staticmethod apenas desabilita a função padrão como descritor de método. O classmethod envolve sua função em um recipiente que pode ser chamado e passa uma referência para a classe proprietária como primeiro argumento:

>>> class C(object):
...  pass
... 
>>> def f():
...  pass
... 
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

De fato, o classmethod tem uma sobrecarga de tempo de execução, mas torna possível acessar a classe proprietária. Como alternativa, recomendo usar uma metaclasse e colocar os métodos de classe nessa metaclasse:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>

Documentos oficiais do python:

@classmethod

Um método de classe recebe a classe como primeiro argumento implícito, assim como um método de instância recebe a instância. Para declarar um método de classe, use este idioma:

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ... 

O formulário @classmethod é um decorator funções - veja a descrição das definições de função em Definições de função para detalhes.

Ele pode ser chamado na classe (como Cf() ) ou em uma instância (como C().f() ). A instância é ignorada, exceto por sua classe. Se um método de classe é chamado para uma classe derivada, o objeto de classe derivada é passado como o primeiro argumento implícito.

Os métodos de classe são diferentes dos métodos estáticos C ++ ou Java. Se você quiser, veja staticmethod() nesta seção.

@staticmethod

Um método estático não recebe um primeiro argumento implícito. Para declarar um método estático, use este idioma:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ... 

O formulário @staticmethod é um decorator funções - veja a descrição das definições de função em Definições de função para detalhes.

Ele pode ser chamado na classe (como Cf() ) ou em uma instância (como C().f() ). A instância é ignorada, exceto por sua classe.

Os métodos estáticos no Python são semelhantes aos encontrados em Java ou C ++. Para um conceito mais avançado, veja classmethod() nesta seção.


método de classe vs método estático em Python

Método de Classe

O decorador @classmethod, é um decorador de função interno que é uma expressão que é avaliada após sua função ser definida. O resultado dessa avaliação obscurece sua definição de função.

Um método de classe recebe a classe como primeiro argumento implícito, assim como um método de instância recebe a instância

Sintaxe:

class C(object):
    @staticmethod
    def fun(arg1, arg2, ...):
        ...
returns: a static method for function fun.
  • Um método de classe é um método que está ligado à classe e não ao objeto da classe.
  • Eles têm acesso ao estado da classe, pois recebem um parâmetro de classe que aponta para a classe e não para a instância do objeto.
  • Pode modificar um estado de classe que se aplicaria a todas as instâncias da classe. Por exemplo, ele pode modificar uma variável de classe que será aplicável a todas as instâncias.

Método Estático

Um método estático não recebe um primeiro argumento implícito.

Sintaxe:

# Python program to demonstrate 
# use of class method and static method.
from datetime import date

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # a class method to create a Person object by birth year.
    @classmethod
    def fromBirthYear(cls, name, year):
        return cls(name, date.today().year - year)

    # a static method to check if a Person is adult or not.
    @staticmethod
    def isAdult(age):
        return age > 18

person1 = Person('mayank', 21)
person2 = Person.fromBirthYear('mayank', 1996)

print person1.age
print person2.age

# print the result
print Person.isAdult(22)
  • Um método estático também é um método vinculado à classe e não ao objeto da classe.
  • Um método estático não pode acessar ou modificar o estado da classe.
  • Está presente em uma classe porque faz sentido que o método esteja presente na classe.

Método de Classe vs Método Estático

  • Um método de classe usa cls como primeiro parâmetro enquanto um método estático não precisa de parâmetros específicos.
  • Um método de classe pode acessar ou modificar o estado da classe enquanto um método estático não pode acessá-lo ou modificá-lo.
  • Usamos o @classmethod decorator em python para criar um método de classe e usamos o @staticmethod decorator para criar um método estático em python.

Quando usar o que?

  • Geralmente, usamos o método de classe para criar métodos de fábrica. Os métodos de fábrica retornam o objeto de classe (semelhante a um construtor) para diferentes casos de uso.
  • Geralmente, usamos métodos estáticos para criar funções de utilidade.

Como definir um método de classe e um método estático?

Para definir um método de classe em python, usamos o @classmethod decorator e, para definir um método estático, usamos o @staticmethod decorator.

Vejamos um exemplo para entender a diferença entre os dois. Vamos dizer que queremos criar uma classe Person. Agora, python não suporta sobrecarga de método como C ++ ou Java, então usamos métodos de classe para criar métodos de fábrica. No exemplo abaixo, usamos um método de classe para criar um objeto pessoa a partir do ano de nascimento.

Como explicado acima, usamos métodos estáticos para criar funções de utilidade. No exemplo abaixo, usamos um método estático para verificar se uma pessoa é adulta ou não.

Implementação

21
21
True

Saída

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)

Reference


Analise o @staticmethod fornecendo literalmente diferentes insights.

Um método normal de uma classe é um método dinâmico implícito que toma a instância como primeiro argumento.
Em contraste, um staticmethod não toma a instância como primeiro argumento, portanto é chamado de 'static' .

Um método estático é de fato uma função tão normal quanto os que estão fora de uma definição de classe.
É felizmente agrupado na classe apenas para ficar mais perto de onde é aplicado, ou você pode rolar para encontrá-lo.


Um rápido avanço de outros métodos idênticos no iPython revela que @staticmethodgera ganhos marginais de desempenho (nos nanossegundos), mas, de outro modo, parece não ter função alguma. Além disso, quaisquer ganhos de desempenho provavelmente serão eliminados pelo trabalho adicional de processamento do método staticmethod()durante a compilação (o que acontece antes de qualquer execução de código quando você executa um script).

Por uma questão de legibilidade do código, eu evitaria, a @staticmethodmenos que seu método fosse usado para cargas de trabalho, onde os nanossegundos contam.


#!/usr/bin/python
#coding:utf-8

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

    @classmethod
    def addone(self, x):
        return x+1

    @staticmethod
    def addtwo(x):
        return x+2

    def addthree(self, x):
        return x+3

def main():
    print Demo.addone(2)
    print Demo.addtwo(2)

    #print Demo.addthree(2) #Error
    demo = Demo(2)
    print demo.addthree(2)


if __name__ == '__main__':
    main()




python-decorators