Qual é a diferença entre uma função decorada com @staticmethod
e uma decorada com @classmethod
?
Qual é a diferença entre uma função decorada com @staticmethod
e uma decorada com @classmethod
?
Respostas:
Talvez um pouco de exemplo de código vai ajudar: 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 de uma instância de objeto chamar 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 os métodos de classe, a classe da instância do objeto é passada implicitamente como o primeiro argumento em vez de self
.
a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
Você também pode ligar class_foo
usando a classe. De fato, se você definir algo para ser um método de classe, provavelmente é porque pretende chamá-lo da classe e não de uma instância de classe. A.foo(1)
teria gerado 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 métodos de classe é criar construtores alternativos herdáveis .
Com métodos estáticos , 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
vinculada como o primeiro argumento da função. foo
espera 2 argumentos, enquanto a.foo
espera apenas 1 argumento.
a
está vinculado a foo
. Isso é o que significa o termo "vinculado" abaixo:
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
Com a.class_foo
, a
não está vinculado a class_foo
, mas a classe A
está vinculada class_foo
.
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>
Aqui, com um método static, mesmo que seja um método, a.static_foo
apenas retorna uma boa função ole sem argumentos vinculados. static_foo
espera 1 argumento e também
a.static_foo
espera 1 argumento.
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
E é claro que a mesma coisa acontece quando você liga static_foo
para a turma A
.
print(A.static_foo)
# <function static_foo at 0xb7d479cc>
@staticmethod
pode ajudar a organizar seu código sendo substituível por subclasses. Sem ele, você teria variantes da função flutuando no espaço de nome do módulo.
@staticmethod
- você pode usá-lo para remover detritos. Estou implementando uma linguagem de programação em Python - as funções definidas pela biblioteca usam um execute
método estático , em que as funções definidas pelo usuário exigem argumentos de instância (ou seja, o corpo da função). Esse decorador elimina avisos "não utilizados nos parâmetros" no inspetor PyCharm.
Um método estático é um método que não sabe nada sobre a classe ou instância em que foi chamado. Apenas obtém os argumentos que foram passados, nenhum primeiro argumento implícito. É basicamente inútil no Python - você pode simplesmente usar uma função de módulo em vez de um método estático.
A classmethod , por outro lado, é um método que é passada a classe que foi chamado, ou a classe da instância que foi chamado, como primeiro argumento. Isso é útil quando você deseja que o método seja uma fábrica para a classe: como ele obtém a classe real na qual foi chamada como primeiro argumento, você sempre pode instanciar a classe correta, mesmo quando há subclasses. Observe, por exemplo dict.fromkeys()
, como , um método de classe, retorna uma instância da subclasse quando chamada em uma subclasse:
>>> class DictSubclass(dict):
... def __repr__(self):
... return "DictSubclass"
...
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>
Basicamente, @classmethod
um método cujo primeiro argumento é a classe da qual é chamado (e não a instância da classe) @staticmethod
não possui argumentos implícitos.
Documentos oficiais do python:
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
@classmethod
formulário é um decorador de funções - consulte a descrição das definições de funções em Definições de funções para obter detalhes.Pode ser chamado na classe (como
C.f()
) ou em uma instância (comoC().f()
). A instância é ignorada, exceto por sua classe. Se um método de classe for chamado para uma classe derivada, o objeto da classe derivada será passado como o primeiro argumento implícito.Os métodos de classe são diferentes dos métodos estáticos em C ++ ou Java. Se você quiser, veja
staticmethod()
nesta seção.
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
@staticmethod
formulário é um decorador de funções - consulte a descrição das definições de funções em Definições de funções para obter detalhes.Pode ser chamado na classe (como
C.f()
) ou em uma instância (comoC().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, consulte
classmethod()
nesta seção.
Aqui está um pequeno artigo sobre esta questão
A função @staticmethod nada mais é do que uma função definida dentro de uma classe. É possível chamar sem instanciar a classe primeiro. Sua definição é imutável por herança.
A função @classmethod também pode ser chamada sem instanciar a classe, mas sua definição segue a subclasse, não a classe pai, por herança. Isso ocorre porque o primeiro argumento para a função @classmethod deve sempre ser cls (class).
Para decidir se deseja usar @staticmethod ou @classmethod, é necessário procurar dentro do seu método. Se 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
cls._counter
ainda seria cls._counter
mesmo se o código fosse colocado em uma classe diferente ou se o nome da classe fosse alterado. Apple._counter
é específico para a Apple
classe; para uma classe diferente ou quando o nome da classe for alterado, você precisará alterar a classe referenciada.
Qual é a diferença entre @staticmethod e @classmethod em 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 documentação 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.
'''
Primeiro eu vou explicar a_normal_instance_method
. Isso é chamado precisamente 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) que é, quando usado, o primeiro dos argumentos é predefinido como a instância do objeto, com todos os seus atributos. Ele tem a instância do objeto vinculada a ele e deve ser chamado de uma instância do objeto. Normalmente, ele acessará vários atributos da instância.
Por exemplo, esta é uma instância de uma sequência:
', '
se usarmos o método de instância, join
nessa string, para ingressar em outro 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'
Os métodos de instância podem ser vinculados por meio de uma pesquisa pontilhada para uso posterior.
Por exemplo, isso vincula o str.join
método à ':'
instância:
>>> join_with_colons = ':'.join
E mais tarde, podemos usar isso como uma função que já tem o primeiro argumento vinculado a ela. Dessa maneira, 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'
O método estático não aceita a instância como argumento.
É muito semelhante a uma função no nível do módulo.
No entanto, uma função no nível do módulo deve residir no módulo e ser importada especialmente para outros locais onde é usada.
Se ele estiver anexado ao objeto, no entanto, ele seguirá o objeto convenientemente por meio de importação e herança também.
Um exemplo de método estático é str.maketrans
movido do string
módulo no Python 3. Torna 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 string
módulo é um pouco desajeitado e é bom poder chamá-lo da classe, como emstr.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ê precisa importar esta função do módulo de string cada vez menos útil:
>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'
Um método de classe é semelhante a um método de instância, pois leva um primeiro argumento implícito, mas, em vez de pegar a instância, leva a classe. Freqüentemente, eles são usados como construtores alternativos para melhor uso semântico e suportam herança.
O exemplo mais canônico de um método de classe interno é dict.fromkeys
. É 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 ditamos uma subclasse, 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 também veja a documentação oficial do Python em classmethod
e staticmethod
.
Comecei a aprender a linguagem de programação com C ++, Java e Python, e essa pergunta também me incomodou muito, até entender o uso simples de cada uma.
Método de classe: Python, ao contrário de Java e C ++, não possui sobrecarga de construtor. E assim, para conseguir isso, você pode usar classmethod
. O exemplo a seguir explicará isso
Vamos considerar que temos uma Person
classe que recebe dois argumentos first_name
e last_name
e cria a instância 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, você precisará criar uma classe usando apenas um nome, apenas a first_name
, não poderá fazer algo assim no Python.
Isso causará 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
o 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: isso é 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 exceda 20 caracteres, basta fazer isso.
@staticmethod
def validate_name(name):
return len(name) <= 20
e você pode simplesmente chamar usando class name
Person.validate_name("Gaurang Shah")
def __init__(self, first_name, last_name="")
vez do método de classe get_person
. Também o resultado será exatamente o mesmo neste caso.
Acho que uma pergunta melhor é "Quando você usaria @classmethod vs @staticmethod?"
O @classmethod permite fácil acesso a membros privados associados à definição de classe. essa é uma ótima maneira de executar singletons ou classes de fábrica que controlam o número de instâncias dos objetos criados.
O @staticmethod fornece ganhos marginais de desempenho, mas ainda tenho que ver um uso produtivo de um método estático dentro de uma classe que não poderia ser alcançado como uma função autônoma fora da classe.
@ decorators foram adicionados no python 2.4 Se você estiver usando python <2.4, poderá usar as funções classmethod () e staticmethod ().
Por exemplo, se você deseja criar um método de fábrica (Uma função retornando uma instância de uma implementação diferente de uma classe, dependendo do argumento recebido), você pode fazer algo como:
class Cluster(object):
def _is_cluster_for(cls, name):
"""
see if this class is the cluster with this name
this is a classmethod
"""
return cls.__name__ == name
_is_cluster_for = classmethod(_is_cluster_for)
#static method
def getCluster(name):
"""
static factory method, should be in Cluster class
returns a cluster object for the given name
"""
for cls in Cluster.__subclasses__():
if cls._is_cluster_for(name):
return cls()
getCluster = staticmethod(getCluster)
Observe também que este é um bom exemplo para o uso de um método de classe e um método estático. O método estático claramente pertence à classe, pois usa a classe Cluster internamente. O método de classe precisa apenas de informações sobre a classe e nenhuma instância do objeto.
Outro benefício de tornar o _is_cluster_for
método um método de classe é que uma subclasse pode decidir mudar sua implementação, talvez porque seja bastante genérica e possa lidar com mais de um tipo de cluster; portanto, apenas verificar o nome da classe não seria suficiente.
Métodos estáticos:
Benefícios dos métodos estáticos:
Mais conveniente para importar versus funções no nível do módulo, pois cada método não precisa ser especialmente importado
@staticmethod
def some_static_method(*args, **kwds):
pass
Métodos de classe:
Eles são criados com a função incorporada do método classmethod.
@classmethod
def some_class_method(cls, *args, **kwds):
pass
@staticmethod
apenas desativa a função padrão como descritor de método. classmethod agrupa sua função em um contêiner que pode ser chamado que passa uma referência à 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'>>
Por uma questão de fato, classmethod
possui uma sobrecarga de tempo de execução, mas possibilita o acesso à classe proprietária. Como alternativa, eu 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'>
c = C(); c.foo()
gera AttributeError, você teria que fazer type(c).foo()
. Isso também pode ser considerado um recurso - não consigo pensar no motivo pelo qual você gostaria.
O guia definitivo sobre como usar métodos estáticos, de classe ou abstratos no Python é um bom link para este tópico e resume-o da seguinte forma.
@staticmethod
A função nada mais é do que uma função definida dentro de uma classe. É possível chamar sem instanciar a classe primeiro. Sua definição é imutável por herança.
@classmethod
A função também pode ser chamada sem instanciar a classe, mas sua definição segue a subclasse, e não a classe pai, por herança, pode ser substituída pela subclasse. Isso ocorre porque o primeiro argumento para a @classmethod
função deve sempre ser cls (classe).
Somente o primeiro argumento difere :
Em mais detalhes...
Quando o método de um objeto é chamado, ele recebe automaticamente um argumento extra self
como seu primeiro argumento. Ou seja, método
def f(self, x, y)
deve ser chamado com 2 argumentos. self
é passado automaticamente e é o próprio objeto .
Quando o método é decorado
@classmethod
def f(cls, x, y)
o argumento fornecido automaticamente não é self
, mas a classe de self
.
Quando o método é decorado
@staticmethod
def f(x, y)
o método não recebe nenhum argumento automático. É dado apenas os parâmetros com os quais é chamado.
classmethod
é usado principalmente para construtores alternativos. staticmethod
não usa o estado do objeto. Pode ser uma função externa a uma classe. Ele é colocado apenas dentro da classe para agrupar funções com funcionalidade semelhante (por exemplo, como Math
métodos estáticos de classe do Java )class Point
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def frompolar(cls, radius, angle):
"""The `cls` argument is the `Point` class itself"""
return cls(radius * cos(angle), radius * sin(angle))
@staticmethod
def angle(x, y):
"""this could be outside the class, but we put it here
just because we think it is logically related to the class."""
return atan(y, x)
p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)
angle = Point.angle(3, 2)
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 , e não apenas na instância da classe. Então, em certo sentido, os dois são métodos de Class .
Diferença: Um método de classe receberá a própria classe como o primeiro argumento, enquanto um método estático não.
Portanto, um método estático não é, de certo modo, vinculado à própria Classe e está apenas lá, apenas porque pode ter uma funcionalidade relacionada.
>>> 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',)
Outra consideração em relação ao staticmethod vs classmethod surge com herança. Digamos que você tenha a seguinte turma:
class Foo(object):
@staticmethod
def bar():
return "In Foo"
E você deseja substituir bar()
em uma classe filho:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
Isso funciona, mas observe que agora a bar()
implementação na classe filho ( Foo2
) não pode mais tirar proveito de nada específico para essa classe. Por exemplo, digamos que Foo2
tenha um método chamado magic()
que você deseja usar na Foo2
implementação de bar()
:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
@staticmethod
def magic():
return "Something useful you'd like to use in bar, but now can't"
A solução aqui seria chamar Foo2.magic()
em bar()
, mas então você está se repetindo (se o nome de Foo2
mudanças, você tem que lembrar de atualizar esse bar()
método).
Para mim, isso é uma pequena violação do princípio de abrir / fechar , pois uma decisão tomada Foo
está afetando sua capacidade de refatorar código comum em uma classe derivada (ou seja, é menos aberta à extensão). Se bar()
fosse um classmethod
, ficaríamos bem:
class Foo(object):
@classmethod
def bar(cls):
return "In Foo"
class Foo2(Foo):
@classmethod
def bar(cls):
return "In Foo2 " + cls.magic()
@classmethod
def magic(cls):
return "MAGIC"
print Foo2().bar()
Dá: In Foo2 MAGIC
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 método próprio, mas pode chamar outro método estático e de classe
3- O método estático pertence à classe e não utilizará nenhum objeto.
4- O método de classe não está vinculado a um objeto, mas a uma classe.
@classmethod: pode ser usado para criar um acesso global compartilhado a todas as instâncias criadas dessa classe ... como atualizar um registro por vários usuários ... Particularmente, achei útil usar ao criar singletons também ..: )
@ método estático: não tem nada a ver com a classe ou instância associada a ... mas, para facilitar a leitura, pode-se usar o método estático
Você pode considerar a diferença entre:
Class A:
def foo(): # no self parameter, no decorator
pass
e
Class B:
@staticmethod
def foo(): # no self parameter
pass
Isso mudou entre python2 e python3:
python2:
>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
python3:
>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
Portanto, o uso @staticmethod
de métodos chamados apenas diretamente da classe tornou-se opcional no python3. Se você quiser chamá-los da classe e da instância, ainda precisará usar o@staticmethod
decorador.
Os outros casos foram bem cobertos pela resposta da unutbus.
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. É um método vinculado à classe e não ao objeto da classe. Ele tem acesso ao estado da classe, pois recebe um parâmetro da classe que aponta para a classe e não para a instância do objeto. Ele 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.
Por outro lado, um método estático não recebe um primeiro argumento implícito, comparado com métodos de classe ou métodos de instância. E não pode acessar ou modificar o estado da classe. Pertence apenas à classe porque, do ponto de vista do design, é o caminho correto. Mas em termos de funcionalidade não está vinculado, em tempo de execução, à classe.
Como orientação, use métodos estáticos como utilitários, use métodos de classe, por exemplo, como fábrica. Ou talvez para definir um singleton. E use métodos de instância para modelar o estado e o comportamento das instâncias.
Espero ter sido claro!
Minha contribuição demonstra a diferença entre @classmethod
, @staticmethod
e métodos de instância, incluindo como uma instância pode indiretamente chamar um @staticmethod
. Mas, em vez de chamar indiretamente um @staticmethod
de uma instância, torná-lo privado pode ser mais "pitô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)
'''
"""
Os métodos de classe, como o nome sugere, são usados para fazer alterações nas classes e não nos objetos. Para fazer alterações nas classes, eles modificarão os atributos da classe (não os atributos do objeto), pois é assim que você atualiza as classes. Esta é a razão pela qual os métodos de classe tomam a classe (convencionalmente denominada por 'cls') como o primeiro argumento.
class A(object):
m=54
@classmethod
def class_method(cls):
print "m is %d" % cls.m
Os métodos estáticos, por outro lado, são usados para executar funcionalidades que não estão vinculadas à classe, ou seja, elas não lêem ou escrevem variáveis da classe. Portanto, métodos estáticos não aceitam classes como argumentos. Eles são usados para que as classes possam executar funcionalidades que não estão diretamente relacionadas ao objetivo da classe.
class X(object):
m=54 #will not be referenced
@staticmethod
def static_method():
print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."
Analisar @staticmethod literalmente fornecendo informações diferentes.
Um método normal de uma classe é um método dinâmico implícito que toma a instância como primeiro argumento.
Por outro lado, um método estático não aceita a instância como primeiro argumento, portanto é chamado de 'estático' .
Um método estático é realmente uma função tão normal quanto as 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.
Eu acho que dar uma versão puramente Python staticmethod
e classmethod
ajudaria a entender a diferença entre eles no nível da linguagem.
Ambos são descritores que não são de dados (seria mais fácil entendê-los se você estiver familiarizado com os descritores primeiro).
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, cls=None):
def inner(*args, **kwargs):
if cls is None:
cls = type(obj)
return self.f(cls, *args, **kwargs)
return inner
staticmethod não tem acesso aos atributos do objeto, da classe ou das classes pai na hierarquia de herança. Pode ser chamado diretamente na classe (sem criar um objeto).
classmethod não tem acesso aos atributos do objeto. No entanto, ele pode acessar atributos da classe e das classes pai na hierarquia de herança. Pode ser chamado diretamente na classe (sem criar um objeto). Se chamado no objeto, é o mesmo que o método normal, que não self.<attribute(s)>
acessa e acessaself.__class__.<attribute(s)>
apenas.
Pense que temos uma classe b=2
, vamos criar um objeto e redefinir isso para b=4
ele. O método estático não pode acessar nada do anterior. O método de classe pode acessar .b==2
apenas, via cls.b
. O método normal pode acessar os dois: .b==4
via self.b
e .b==2
via self.__class__.b
.
Poderíamos seguir o estilo KISS (simplifique, seja estúpido): Não use métodos estáticos e métodos de classe, não use classes sem instanciar, acesse apenas os atributos do objeto self.attribute(s)
. Existem idiomas em que o OOP é implementado dessa maneira e acho que não é uma má idéia. :)
Uma rápida invasão de métodos idênticos no iPython revela que @staticmethod
gera ganhos marginais de desempenho (em nanossegundos), mas, caso contrário, parece não ter nenhuma função. Além disso, quaisquer ganhos de desempenho provavelmente serão eliminados pelo trabalho adicional de processar o método staticmethod()
durante a compilação (o que ocorre 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 @staticmethod
menos que seu método fosse usado para muito trabalho, onde os nanossegundos são importantes.