Diferencia entre método estático y método de clase

4052

¿Cuál es la diferencia entre una función decorada @staticmethody una decorada @classmethod?

4
  • 17
    Los métodos estáticos a veces son mejores como funciones de nivel de módulo en Python por motivos de limpieza. Con una función de módulo es más fácil importar solo la función que necesita y evitar "" innecesarios. sintaxis (te estoy mirando Objective-C). Los métodos de clase tienen más uso ya que se pueden usar en combinación con polimorfismo para crear funciones de "patrón de fábrica". esto se debe a que los métodos de clase reciben la clase como un parámetro implícito. 6 de diciembre de 2017 a las 3:44
  • 37
    tl; dr >> en comparación con los métodos normales, también se puede acceder a los métodos estáticos y a los métodos de clase utilizando la clase, pero a diferencia de los métodos de clase, los métodos estáticos son inmutables por herencia. 11/07/18 a las 15:02
  • 4
    Charla relacionada de Raymond Hettinger sobre el tema: youtube.com/watch?v=HTLu2DFOdTg 10 de septiembre de 2018 a las 9:35
  • 1
    youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 más preciso solo necesita un método de clase para constructores alternativos. De lo contrario, puede usar staticmethod y acceder a cualquier atributo de clase (a través de. / Dot) mediante el "CLASSNAME" real de alguna manera más informativo en lugar de cls como en classmethod 11/10/19 a las 11:22
3493

Tal vez un poco de ejemplo de código ayudará a: Aviso de la diferencia de las firmas de llamadas foo, class_fooy static_foo:

class A(object):
    def foo(self, x):
        print(f"executing foo({self}, {x})")

    @classmethod
    def class_foo(cls, x):
        print(f"executing class_foo({cls}, {x})")

    @staticmethod
    def static_foo(x):
        print(f"executing static_foo({x})")

a = A()

A continuación se muestra la forma habitual en que una instancia de objeto llama a un método. La instancia del objeto,, ase pasa implícitamente como primer argumento.

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

Con classmethods , la clase de la instancia del objeto se pasa implícitamente como primer argumento en lugar de self.

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

También puede llamar class_foousando la clase. De hecho, si define algo para que sea un método de clase, probablemente sea porque tiene la intención de llamarlo desde la clase en lugar de desde una instancia de clase. A.foo(1)habría generado un TypeError, pero A.class_foo(1)funciona bien:

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

Un uso que la gente ha encontrado para los métodos de clase es crear constructores alternativos heredables .


Con los métodos estáticos , ni self(la instancia del objeto) ni cls(la clase) se pasan implícitamente como primer argumento. Se comportan como funciones simples, excepto que puede llamarlas desde una instancia o la clase:

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

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

Los métodos estáticos se utilizan para agrupar funciones que tienen alguna conexión lógica con una clase a la clase.


fooes solo una función, pero cuando llamas a.foono solo obtienes la función, obtienes una versión "parcialmente aplicada" de la función con la instancia del objeto aenlazada como el primer argumento de la función. fooespera 2 argumentos, mientras que a.foosolo espera 1 argumento.

aestá obligado a foo. Eso es lo que se entiende por el término "vinculado" a continuación:

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

Con a.class_foo, ano está vinculado a class_foo, más bien la clase Aestá vinculada a class_foo.

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

Aquí, con un método estático, aunque es un método, a.static_foosimplemente devuelve una buena función 'ole sin argumentos vinculados. static_fooespera 1 argumento y también a.static_fooespera 1 argumento.

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

Y, por supuesto, sucede lo mismo cuando llama static_foocon la clase A.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>
4
  • 213
    No entiendo cuál es la trampa para usar el método estático. simplemente podemos usar una función simple fuera de clase.
    Alcott
    19 de septiembre de 2011 a las 3:08
  • 433
    @Alcott: es posible que desee mover una función a una clase porque lógicamente pertenece a la clase. En el código fuente de Python (por ejemplo, multiprocesamiento, tortuga, paquetes dist), se usa para "ocultar" funciones "privadas" de un solo subrayado del espacio de nombres del módulo. Sin embargo, su uso está altamente concentrado en solo unos pocos módulos, quizás una indicación de que es principalmente una cuestión estilística. Aunque no pude encontrar ningún ejemplo de esto, @staticmethodpodría ayudar a organizar su código al ser reemplazado por subclases. Sin él, tendría variantes de la función flotando en el espacio de nombres del módulo.
    unutbu
    19/09/11 a las 10:34
  • 21
    ... junto con una explicación sobre dónde y por qué utilizar métodos de instancia, clase o estáticos. No dijiste una sola palabra al respecto, pero el OP tampoco preguntó al respecto. 3 de mayo de 2012 a las 9:50
  • 124
    @Alcott: como dijo unutbu, los métodos estáticos son una característica organizativa / estilística. A veces, un módulo tiene muchas clases, y algunas funciones auxiliares están vinculadas lógicamente a una clase determinada y no a las demás, por lo que tiene sentido no "contaminar" el módulo con muchas "funciones libres", y es mejor usar una clase estática. método que confiar en el estilo deficiente de mezclar clases y definiciones de funciones juntas en el código solo para mostrar que están "relacionadas" 3 de mayo de 2012 a las 9:55
869

Un método estático es un método que no sabe nada sobre la clase o instancia a la que se llamó. Solo obtiene los argumentos que se pasaron, sin un primer argumento implícito. Básicamente es inútil en Python: puede usar una función de módulo en lugar de un método estático.

Un método de clase , por otro lado, es un método que se pasa la clase a la que se llamó, o la clase de la instancia a la que se llamó, como primer argumento. Esto es útil cuando desea que el método sea una fábrica para la clase: dado que obtiene la clase real a la que se llamó como primer argumento, siempre puede crear una instancia de la clase correcta, incluso cuando hay subclases involucradas. Observe, por ejemplo dict.fromkeys(), cómo , un método de clase, devuelve una instancia de la subclase cuando se llama en una subclase:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 
9
  • 772
    Un método estático no es inútil: es una forma de poner una función en una clase (porque lógicamente pertenece allí), al tiempo que indica que no requiere acceso a la clase. 26 de septiembre de 2008 a las 10:10
  • 148
    De ahí que sólo sea "básicamente" inútil. Dicha organización, así como la inyección de dependencias, son usos válidos de métodos estáticos, pero dado que los módulos, no las clases como en Java, son los elementos básicos de la organización del código en Python, su uso y utilidad es poco común. 26 de septiembre de 2008 a las 13:40
  • 44
    ¿Qué tiene de lógico definir un método dentro de una clase, cuando no tiene nada que ver ni con la clase ni con sus instancias? 12 mar.10 a las 9:11
  • 114
    ¿Quizás por el bien de la herencia? Los métodos estáticos se pueden heredar y anular al igual que los métodos de instancia y los métodos de clase, y la búsqueda funciona como se esperaba (a diferencia de Java). Los métodos estáticos no se resuelven realmente estáticamente ya sea que se llamen en la clase o en la instancia, por lo que la única diferencia entre los métodos de clase y estáticos es el primer argumento implícito.
    haridsv
    8 de abril de 2010 a las 1:32
  • 88
    También crean un espacio de nombres más limpio y facilitan la comprensión de que la función tiene algo que ver con la clase. 22/08/11 a las 11:58
172

Básicamente, @classmethodcrea un método cuyo primer argumento es la clase desde la que se llama (en lugar de la instancia de la clase), @staticmethodno tiene ningún argumento implícito.

117

Documentos oficiales de Python:

@classmethod

A class method receives the class as implicit first argument, just like an instance method receives the instance. To declare a class method, use this idiom:

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

The @classmethod form is a function decorator – see the description of function definitions in Function definitions for details.

It can be called either on the class (such as C.f()) or on an instance (such as C().f()). The instance is ignored except for its class. If a class method is called for a derived class, the derived class object is passed as the implied first argument.

Class methods are different than C++ or Java static methods. If you want those, see staticmethod() in this section.

@staticmethod

A static method does not receive an implicit first argument. To declare a static method, use this idiom:

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

The @staticmethod form is a function decorator – see the description of function definitions in Function definitions for details.

It can be called either on the class (such as C.f()) or on an instance (such as C().f()). The instance is ignored except for its class.

Static methods in Python are similar to those found in Java or C++. For a more advanced concept, see classmethod() in this section.

2
  • ¿No hay un error en los documentos? No debería estar en método estático: "La instancia y su clase se ignoran". en lugar de "La instancia se ignora excepto por su clase".
    mirek
    2 mar.20 a las 15:33
  • Puede ser un error de cortar y pegar, pero estrictamente hablando, no puede llamar a un método en una clase si ignora la clase. 15 de mayo de 2020 a las 1:54
91

Aquí hay un breve artículo sobre esta cuestión.

@staticmethod function is nothing more than a function defined inside a class. It is callable without instantiating the class first. It’s definition is immutable via inheritance.

@classmethod function also callable without instantiating the class, but its definition follows Sub class, not Parent class, via inheritance. That’s because the first argument for @classmethod function must always be cls (class).

4
  • 2
    Entonces, ¿eso significa que al usar un método estático siempre estoy vinculado a la clase principal y con el método de clase estoy vinculado a la clase en la que declaro el método de clase (en este caso, la subclase)? 3 de nov. De 2009 a las 19:06
  • 8
    No. Al usar un método estático, no estás limitado en absoluto; no hay un primer parámetro implícito. Al usar classmethod, obtiene como primer parámetro implícito la clase a la que llamó al método (si la llamó directamente en una clase), o la clase de la instancia a la que llamó al método (si la llamó a una instancia). 3 de nov. De 2009 a las 19:18
  • 8
    Podría ampliarse un poco para mostrar que, al tener una clase como primer argumento, los métodos de clase tienen acceso directo a otros atributos y métodos de clase, mientras que los métodos estáticos no lo tienen (necesitarían codificar MyClass.attr para eso) 3 de mayo de 2012 a las 10:01
  • 1
    "Su definición es inmutable a través de la herencia". no tiene ningún sentido en Python, puede anular un método estático sin problemas.
    c z
    17/09/19 a las 15:20
85

Para decidir si usar @staticmethod o @classmethod , debe mirar dentro de su método. Si su método accede a otras variables / métodos en su clase, use @classmethod . Por otro lado, si su método no toca ninguna otra parte de la clase, utilice @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
2
  • cuál sería la ventaja de classmethod y cls._counter frente a staticmethod y Apple._counter 11/10/19 a las 10:38
  • 2
    cls._counteraún lo sería cls._counterincluso si el código se coloca en una clase diferente, o si se cambia el nombre de la clase. Apple._counteres específico para la Appleclase; para una clase diferente, o cuando se cambia el nombre de la clase, deberá cambiar la clase a la que se hace referencia. 12/11/19 a las 22:55
64

What is the difference between @staticmethod and @classmethod in Python?

Es posible que haya visto código Python como este pseudocódigo, que demuestra las firmas de los distintos tipos de métodos y proporciona una cadena de documentación para explicar cada uno:

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

El método de instancia normal

Primero te lo explicaré a_normal_instance_method. Esto se denomina precisamente " método de instancia ". Cuando se usa un método de instancia, se usa como una función parcial (a diferencia de una función total, definida para todos los valores cuando se ve en el código fuente) es decir, cuando se usa, el primero de los argumentos está predefinido como la instancia del objeto, con todos sus atributos dados. Tiene la instancia del objeto vinculada a él y debe llamarse desde una instancia del objeto. Normalmente, accederá a varios atributos de la instancia.

Por ejemplo, esta es una instancia de una cadena:

', '

si usamos el método de instancia, joinen esta cadena, para unirse a otro iterable, que, obviamente, es una función de la instancia, además de ser una función de la lista de iterable, ['a', 'b', 'c']:

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

Métodos ligados

Los métodos de instancia se pueden vincular mediante una búsqueda de puntos para su uso posterior.

Por ejemplo, esto vincula el str.joinmétodo a la ':'instancia:

>>> join_with_colons = ':'.join 

Y luego podemos usar esto como una función que ya tiene el primer argumento vinculado a ella. De esta forma, funciona como una función parcial en la instancia:

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

El método estático no toma la instancia como argumento.

Es muy similar a una función de nivel de módulo.

Sin embargo, una función de nivel de módulo debe residir en el módulo y ser especialmente importada a otros lugares donde se utilice.

Sin embargo, si está adjunto al objeto, lo seguirá convenientemente a través de la importación y la herencia también.

Un ejemplo de un método estático es str.maketrans, movido desde el stringmódulo en Python 3. Hace que una tabla de traducción sea adecuada para el consumo de str.translate. Parece bastante tonto cuando se usa desde una instancia de una cadena, como se muestra a continuación, pero importar la función desde el stringmódulo es bastante torpe, y es bueno poder llamarlo desde la clase, como enstr.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}

En python 2, debe importar esta función desde el módulo de cadena cada vez menos útil:

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

Método de clase

Un método de clase es similar a un método de instancia en que toma un primer argumento implícito, pero en lugar de tomar la instancia, toma la clase. Con frecuencia, estos se utilizan como constructores alternativos para un mejor uso semántico y admitirán la herencia.

El ejemplo más canónico de un método de clase incorporado es dict.fromkeys. Se utiliza como un constructor alternativo de dict (muy adecuado para cuando sabe cuáles son sus claves y desea un valor predeterminado para ellas).

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

Cuando hacemos una subclase dict, podemos usar el mismo constructor, que crea una instancia de la subclase.

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

Consulte el código fuente de pandas para ver otros ejemplos similares de constructores alternativos y consulte también la documentación oficial de Python en classmethody staticmethod.

0
54

Comencé a aprender lenguaje de programación con C ++ y luego Java y luego Python, por lo que esta pregunta también me molestó mucho, hasta que entendí el uso simple de cada uno.

Método de clase: Python, a diferencia de Java y C ++, no tiene sobrecarga de constructores. Y entonces, para lograr esto, podrías usar classmethod. El siguiente ejemplo explicará esto

Vamos a considerar que tenemos una Personclase que toma dos argumentos first_namey last_namey crea la instancia de Person.

class Person(object):

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

Ahora, si el requisito llega cuando necesita crear una clase con un solo nombre, solo a first_name, no puede hacer algo como esto en Python.

Esto le dará un error cuando intente crear un objeto (instancia).

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

Sin embargo, puede lograr lo mismo usando @classmethodcomo se menciona a continuación

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: esto es bastante simple, no está vinculado a una instancia o clase y simplemente puede llamarlo usando el nombre de la clase.

Entonces, digamos en el ejemplo anterior que necesita una validación que first_nameno debe exceder los 20 caracteres, simplemente puede hacer esto.

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

y podrías simplemente llamar usando class name

Person.validate_name("Gaurang Shah")
1
  • 7
    Es una publicación antigua, pero se usaría una forma más pitónica de lograr que el constructor acepte uno o dos argumentos en def __init__(self, first_name, last_name="")lugar del método de clase get_person. También el resultado será exactamente el mismo en este caso. 17/09/19 a las 22:08
40

Creo que una mejor pregunta es "¿Cuándo usarías @classmethodvs @staticmethod?"

@classmethodle permite acceder fácilmente a los miembros privados que están asociados a la definición de clase. esta es una excelente manera de hacer singletons o clases de fábrica que controlan el número de instancias de los objetos creados que existen.

@staticmethod proporciona ganancias de rendimiento marginales, pero todavía no he visto un uso productivo de un método estático dentro de una clase que no se podría lograr como una función independiente fuera de la clase.

1
  • Esta pregunta menciona el acceso de miembros de clases privadas. Quiero resaltar que (dependiendo de su definición de privado), los @staticmethodcorreos electrónicos tienen un nivel de acceso diferente al de los correos @classmethodelectrónicos. El primero no debe acceder a los miembros de la clase privada de la clase.
    joel
    13/08/20 a las 21:37
37

Métodos estáticos:

  • Funciones simples sin argumentos propios.
  • Trabajar en los atributos de la clase; no en atributos de instancia.
  • Se puede llamar tanto a través de clase como de instancia.
  • La función incorporada staticmethod () se usa para crearlos.

Beneficios de los métodos estáticos:

  • Localiza el nombre de la función en el classscope
  • Mueve el código de función más cerca de donde se usa
  • Es más conveniente importar que las funciones a nivel de módulo, ya que no es necesario importar cada método de forma especial.

    @staticmethod
    def some_static_method(*args, **kwds):
        pass
    

Métodos de clase:

  • Funciones que tienen el primer argumento como nombre de clase.
  • Se puede llamar tanto a través de clase como de instancia.
  • Estos se crean con la función incorporada classmethod.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass
    
36

Solo el primer argumento difiere :

  • método normal: el objeto actual se pasa automáticamente como un primer argumento (adicional)
  • método de clase : la clase del objeto actual se pasa automáticamente como un primer argumento (adicional)
  • método estático: no se pasan automáticamente argumentos adicionales . Lo que le pasó a la función es lo que obtiene.

Con más detalle...

método normal

El método "estándar", como en todos los lenguajes orientados a objetos. Cuando se llama al método de un objeto, se le da automáticamente un argumento adicional selfcomo primer argumento. Es decir, método

def f(self, x, y)

debe llamarse con 2 argumentos. selfse pasa automáticamente y es el objeto en sí . Similar al thisque aparece mágicamente en eg. java / c ++, solo en Python se muestra explícitamente.

actually, the first argument does not have to be called self, but it's the standard convention, so keep it

método de clase

Cuando el método está decorado

@classmethod
def f(cls, x, y)

el argumento proporcionado automáticamente no es self , sino la clase de self .

método estático

Cuando el método está decorado

@staticmethod
def f(x, y)

el método no recibe ningún argumento automático en absoluto. Solo se le dan los parámetros con los que se llama.

usos

  • classmethod se utiliza principalmente para constructores alternativos.
  • staticmethodno usa el estado del objeto, ni siquiera la estructura de la clase en sí. Podría ser una función externa a una clase. Solo se coloca dentro de la clase para agrupar funciones con una funcionalidad similar (por ejemplo, como los Mathmétodos estáticos de clase de 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)

34

Se agregaron @decorators en python 2.4. Si está usando python <2.4, puede usar la función classmethod () y staticmethod ().

Por ejemplo, si desea crear un método de fábrica (una función que devuelve una instancia de una implementación diferente de una clase según el argumento que obtenga), puede hacer 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)

También observe que este es un buen ejemplo para usar un método de clase y un método estático. El método estático claramente pertenece a la clase, ya que usa la clase Cluster internamente. El método de clase solo necesita información sobre la clase y ninguna instancia del objeto.

Otro beneficio de convertir el _is_cluster_formétodo en un método de clase es que una subclase puede decidir cambiar su implementación, tal vez porque es bastante genérico y puede manejar más de un tipo de clúster, por lo que solo verificar el nombre de la clase no sería suficiente.

30

Déjame decirte la similitud entre un método decorado con @classmethod vs @staticmethod primero.

Similitud: ambos se pueden invocar en la propia clase , en lugar de solo en la instancia de la clase. Entonces, ambos en cierto sentido son métodos de Class .

Diferencia: un método de clase recibirá la clase en sí como primer argumento, mientras que un método estático no.

Entonces, un método estático, en cierto sentido, no está vinculado a la Clase en sí y simplemente permanece ahí solo porque puede tener una funcionalidad 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',)
25

@staticmethodsimplemente desactiva la función predeterminada como descriptor de método. classmethod envuelve su función en un contenedor invocable que pasa una referencia a la clase propietaria como primer 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 hecho, classmethodtiene una sobrecarga de tiempo de ejecución pero permite acceder a la clase propietaria. Alternativamente, recomiendo usar una metaclase y poner los métodos de clase en esa metaclase:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>
1
  • 1
    Una posible desventaja de una metaclase para esto que se me ocurre de inmediato es que no se puede llamar al método de clase directamente en una instancia. c = C(); c.foo()plantea AttributeError, tendrías que hacerlo type(c).foo(). Esto también podría considerarse una característica, aunque no puedo pensar en por qué querrías hacerlo.
    Aaron Hall
    14/06/2016 a las 21:57
23

La guía definitiva sobre cómo usar métodos estáticos, de clase o abstractos en Python es un buen enlace para este tema, y ​​lo resume de la siguiente manera.

@staticmethodfunción no es más que una función definida dentro de una clase. Es invocable sin instanciar la clase primero. Su definición es inmutable por herencia.

  • Python no tiene que crear una instancia de un método vinculado para el objeto.
  • Facilita la legibilidad del código y no depende del estado del objeto en sí;

@classmethodLa función también se puede llamar sin instanciar la clase, pero su definición sigue a la clase Sub, no a la clase principal, a través de la herencia, puede ser anulada por una subclase. Eso es porque el primer argumento de la @classmethodfunción siempre debe ser cls (clase).

  • Métodos de fábrica , que se utilizan para crear una instancia para una clase utilizando, por ejemplo, algún tipo de preprocesamiento.
  • Métodos estáticos que llaman a métodos estáticos : si divide un método estático en varios métodos estáticos, no debe codificar el nombre de la clase, sino utilizar métodos de clase.
1
  • Gracias @zangw: la inmutabilidad heredada de la función estática es la diferencia clave que parece 2 de mayo de 2018 a las 3:38
20

Otra consideración con respecto al método estático frente al método de clase surge con la herencia. Digamos que tienes la siguiente clase:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

Y luego desea anular bar()en una clase secundaria:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

Esto funciona, pero tenga en cuenta que ahora la bar()implementación en la clase secundaria ( Foo2) ya no puede aprovechar nada específico de esa clase. Por ejemplo, digamos que Foo2tenía un método llamado magic()que desea utilizar en la Foo2implementación 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" 

La solución en este caso sería llamar Foo2.magic()en bar(), pero luego te estás repitiendo (si el nombre de Foo2los cambios, tendrá que acordarse de actualizar ese bar()método).

Para mí, esta es una ligera violación del principio abierto / cerrado , ya que una decisión tomada en Fooestá afectando su capacidad para refactorizar el código común en una clase derivada (es decir, está menos abierto a la extensión). Si bar()fuera un classmethodestaríamos bien:

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

Da: In Foo2 MAGIC

También: nota histórica: Guido Van Rossum (el creador de Python) una vez se refirió a staticmethod's como "un accidente": https://mail.python.org/pipermail/python-ideas/2012-May/014969.html

we all know how limited static methods are. (They're basically an accident -- back in the Python 2.2 days when I was inventing new-style classes and descriptors, I meant to implement class methods but at first I didn't understand them and accidentally implemented static methods first. Then it was too late to remove them and only provide class methods.

También: https://mail.python.org/pipermail/python-ideas/2016-July/041189.html

Honestly, staticmethod was something of a mistake -- I was trying to do something like Java class methods but once it was released I found what was really needed was classmethod. But it was too late to get rid of staticmethod.

9

Intentaré explicar la diferencia básica con un ejemplo.

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 llamar directamente a métodos estáticos y de clase sin inicializar

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

2- El método estático no puede llamar al método self pero puede llamar a otro método estático y de clase

3- El método estático pertenece a la clase y no usará ningún objeto.

4- Los métodos de clase no están vinculados a un objeto sino a una clase.

1
  • 1
    anuncio 2: ¿Estás seguro? ¿Cómo podría el método estático llamar al método de clase? No tiene ninguna referencia a él (a su clase).
    mirek
    2 mar.20 a las 16:31
7

@classmethod: se puede usar para crear un acceso global compartido a todas las instancias creadas de esa clase ... como actualizar un registro por varios usuarios ... Particularmente lo encontré útil al crear singletons también ...: )

@método estático: no tiene nada que ver con la clase o instancia con la que se asocia ... pero para mejorar la legibilidad, puede usar el método estático

7

Un método de clase recibe la clase como primer argumento implícito, al igual que un método de instancia recibe la instancia. Es un método que está vinculado a la clase y no al objeto de la clase. Tiene acceso al estado de la clase ya que toma un parámetro de clase que apunta a la clase y no a la instancia del objeto. Puede modificar un estado de clase que se aplicaría en todas las instancias de la clase. Por ejemplo, puede modificar una variable de clase que será aplicable a todas las instancias.

Por otro lado, un método estático no recibe un primer argumento implícito, en comparación con los métodos de clase o de instancia. Y no puede acceder ni modificar el estado de la clase. Solo pertenece a la clase porque desde el punto de vista del diseño esa es la forma correcta. Pero en términos de funcionalidad no está vinculado, en tiempo de ejecución, a la clase.

como guía, use métodos estáticos como utilidades, use métodos de clase, por ejemplo, como fábrica. O quizás para definir un singleton. Y use métodos de instancia para modelar el estado y el comportamiento de las instancias.

¡Espero haber sido claro!

7

Es posible que desee considerar la diferencia entre:

class A:
    def foo():  # no self parameter, no decorator
        pass

y

class B:
    @staticmethod
    def foo():  # no self parameter
        pass

Esto ha cambiado entre python2 y python3:

python2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

Por lo tanto, el uso @staticmethodde métodos que solo se llaman directamente desde la clase se ha vuelto opcional en python3. Si desea llamarlos tanto desde la clase como desde la instancia, aún necesita usar el @staticmethoddecorador.

Los otros casos han sido bien cubiertos por la respuesta de Unutbus.

7

La diferencia ocurre cuando hay herencia.

Suponga que hay dos clases: padre e hijo. Si uno quiere usar @staticmethod, el método print_name debe escribirse dos veces porque el nombre de la clase debe escribirse en la línea de impresión.

class Parent:
   _class_name = "Parent"

   @staticmethod
   def print_name():
       print(Parent._class_name)


class Child(Parent):
   _class_name = "Child"

   @staticmethod
   def print_name():
       print(Child._class_name)


Parent.print_name()
Child.print_name()

Sin embargo, para @classmethod, no es necesario escribir dos veces el método print_name.

class Parent:
    _class_name = "Parent"

    @classmethod
    def print_name(cls):
        print(cls._class_name)


class Child(Parent):
    _class_name = "Child"


Parent.print_name()
Child.print_name()
1
  • Me gustó mucho la sencillez de esta respuesta. Wish podría haberle dado 100 votos.
    masih
    29 de julio a las 12:34
5

Mi contribución demuestra la diferencia entre @classmethod, @staticmethody los métodos de instancia, incluyendo cómo una instancia puede llamar indirectamente a una @staticmethod. Pero en lugar de llamar indirectamente a @staticmethoddesde una instancia, hacerlo privado puede ser más "pitónico". Obtener algo de un método privado no se demuestra aquí, pero es básicamente el mismo concepto.

#!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)
'''
"""
5

Método de instancia :

+ Puede modificar el estado de la instancia del objeto

+ Puede modificar el estado de la clase

Método de clase :

- No se puede modificar el estado de la instancia del objeto

+ Puede modificar el estado de la clase

Método estático :

- No se puede modificar el estado de la instancia del objeto

- No se puede modificar el estado de la clase

class MyClass:
    ''' 
    Instance method has a mandatory first attribute self which represent the instance itself. 
    Instance method must be called by a instantiated instance.
    '''
    def method(self):
        return 'instance method called', self
    
    '''
    Class method has a mandatory first attribute cls which represent the class itself. 
    Class method can be called by an instance or by the class directly. 
    Its most common using scenario is to define a factory method.
    '''
    @classmethod
    def class_method(cls):
        return 'class method called', cls
    
    '''
    Static method doesn’t have any attributes of instances or the class. 
    It also can be called by an instance or by the class directly. 
    Its most common using scenario is to define some helper or utility functions which are closely relative to the class.
    '''
    @staticmethod
    def static_method():
        return 'static method called'


obj = MyClass()
print(obj.method())
print(obj.class_method()) # MyClass.class_method()
print(obj.static_method()) # MyClass.static_method()

producción:

('instance method called', <__main__.MyClass object at 0x100fb3940>)
('class method called', <class '__main__.MyClass'>)
static method called

El método de instancia en realidad teníamos acceso a la instancia del objeto, así que esta era una instancia de un objeto de mi clase, mientras que con el método de clase tenemos acceso a la clase en sí. Pero no a ninguno de los objetos, porque el método de clase realmente no se preocupa por la existencia de un objeto. Sin embargo, puede llamar a un método de clase y a un método estático en una instancia de objeto. Esto va a funcionar, realmente no hace una diferencia, por lo que nuevamente cuando llame al método estático aquí, funcionará y sabrá a qué método desea llamar.

Los métodos estáticos se utilizan para realizar algunas tareas de utilidad y los métodos de clase se utilizan para los métodos de fábrica. Los métodos de fábrica pueden devolver objetos de clase para diferentes casos de uso.

Y finalmente, un pequeño ejemplo para una mejor comprensión:

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

    @classmethod
    def get_from_string(cls, name_string: str):
        first_name, last_name = name_string.split()
        if Student.validate_name(first_name) and Student.validate_name(last_name):
            return cls(first_name, last_name)
        else:
            print('Invalid Names')

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


stackoverflow_student = Student.get_from_string('Name Surname')
print(stackoverflow_student.first_name) # Name
print(stackoverflow_student.last_name) # Surname
3

Analice @staticmethod literalmente proporcionando diferentes perspectivas.

Un método normal de una clase es un método dinámico implícito que toma la instancia como primer argumento.
Por el contrario, un método estático no toma la instancia como primer argumento, por lo que se llama 'estático' .

Un método estático es de hecho una función tan normal como las que están fuera de una definición de clase.
Afortunadamente, está agrupado en la clase solo para estar más cerca de donde se aplica, o puede desplazarse para encontrarlo.

0
3

Creo que dar una versión puramente Python de staticmethody classmethodayudaría a comprender la diferencia entre ellos a nivel de lenguaje.

Ambos son descriptores que no son de datos (sería más fácil entenderlos si primero está familiarizado con los descriptores ).

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
2

Los métodos de clase, como su nombre indica, se utilizan para realizar cambios en las clases y no en los objetos. Para realizar cambios en las clases, modificarán los atributos de la clase (no los atributos del objeto), ya que así es como se actualizan las clases. Esta es la razón por la que los métodos de clase toman la clase (denotada convencionalmente por 'cls') como primer argumento.

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

Los métodos estáticos, por otro lado, se utilizan para realizar funcionalidades que no están vinculadas a la clase, es decir, no leerán ni escribirán variables de clase. Por tanto, los métodos estáticos no toman clases como argumentos. Se utilizan para que las clases puedan realizar funcionalidades que no están directamente relacionadas con el propósito de la clase.

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."
1
  • los métodos no siempre hacen cambios
    joel
    13/08/20 a las 21:19
1

Se produce una diferencia práctica bastante importante al realizar subclases. Si no le importa, secuestraré el ejemplo de @ unutbu:

class A: 
    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)

class B(A):
    pass

En class_foo, el método sabe en qué clase se llama:

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

En static_foo, no hay forma de determinar si se solicita Ao B:

A.static_foo(1)
# => executing static_foo(1)
B.static_foo(1)
# => executing static_foo(1)

Tenga en cuenta que esto no significa que no pueda usar otros métodos en a staticmethod, solo tiene que hacer referencia a la clase directamente, lo que significa que los métodos estáticos de las subclases seguirán haciendo referencia a la clase principal:

class A:
    @classmethod
    def class_qux(cls, x):
        print(f"executing class_qux({cls}, {x})")
    
    @classmethod
    def class_bar(cls, x):
        cls.class_qux(x)

    @staticmethod
    def static_bar(x):
        A.class_qux(x)

class B(A):
    pass

A.class_bar(1)
# => executing class_qux(<class '__main__.A'>, 1)
B.class_bar(1)
# => executing class_qux(<class '__main__.B'>, 1)
A.static_bar(1)
# => executing class_qux(<class '__main__.A'>, 1)
B.static_bar(1)
# => executing class_qux(<class '__main__.A'>, 1)
1

Primero comencemos con un código de ejemplo que usaremos para comprender ambos conceptos:

class Employee:

    NO_OF_EMPLOYEES = 0
  
    def __init__(self, first_name, last_name, salary):
        self.first_name = first_name
        self.last_name = last_name
        self.salary = salary
        self.increment_employees()

    def give_raise(self, amount):
        self.salary += amount

    @classmethod
    def employee_from_full_name(cls, full_name, salary):
        split_name = full_name.split(' ')
        first_name = split_name[0]
        last_name = split_name[1]
        return cls(first_name, last_name, salary)

    @classmethod
    def increment_employees(cls):
        cls.NO_OF_EMPLOYEES += 1

    @staticmethod
    def get_employee_legal_obligations_txt():
        legal_obligations = """
        1. An employee must complete 8 hours per working day
        2. ...
        """
        return legal_obligations

Método de clase

Un método de clase acepta la clase en sí misma como un argumento implícito y, opcionalmente, cualquier otro argumento especificado en la definición. Es importante entender que un método de clase no tiene acceso a instancias de objetos (como lo hacen los métodos de instancia). Por lo tanto, los métodos de clase no se pueden usar para alterar el estado de un objeto instanciado, sino que son capaces de cambiar el estado de la clase que se comparte entre todas las instancias de esa clase. Los métodos de clase suelen ser útiles cuando necesitamos acceder a la propia clase; por ejemplo, cuando queremos crear un método de fábrica, es un método que crea instancias de la clase. En otras palabras, los métodos de clase pueden servir como constructores alternativos.

En nuestro código de ejemplo, Employeese puede construir una instancia de proporcionando tres argumentos; first_name, last_nameY salary.

employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.first_name)
print(employee_1.salary)

'Andrew'
85000

Ahora supongamos que existe la posibilidad de que el nombre de un empleado se pueda proporcionar en un solo campo en el que el nombre y el apellido estén separados por un espacio en blanco. En este caso, posiblemente podríamos usar nuestro método de clase llamado employee_from_full_nameque acepta tres argumentos en total. El primero es la clase en sí, que es un argumento implícito, lo que significa que no se proporcionará al llamar al método; Python lo hará automáticamente por nosotros:

employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(employee_2.first_name)
print(employee_2.salary)

'John'
95000

Tenga en cuenta que también es posible llamar employee_from_full_namedesde instancias de objetos, aunque en este contexto no tiene mucho sentido:

employee_1 = Employee('Andrew', 'Brown', 85000)
employee_2 = employee_1.employee_from_full_name('John Black', 95000)

Otra razón por la que podríamos querer crear un método de clase es cuando necesitamos cambiar el estado de la clase. En nuestro ejemplo, la variable de clase NO_OF_EMPLOYEESrealiza un seguimiento del número de empleados que trabajan actualmente para la empresa. Este método se llama cada vez que se crea una nueva instancia de Employee y actualiza el recuento en consecuencia:

employee_1 = Employee('Andrew', 'Brown', 85000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')
employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')

Number of employees: 1
Number of employees: 2

Métodos estáticos

Por otro lado, en los métodos estáticos ni la instancia (es decir self) ni la clase en sí (es decir cls) se pasan como un argumento implícito. Esto significa que dichos métodos no son capaces de acceder a la clase en sí ni a sus instancias. Ahora se podría argumentar que los métodos estáticos no son útiles en el contexto de las clases, ya que también se pueden colocar en módulos auxiliares en lugar de agregarlos como miembros de la clase. En la programación orientada a objetos, es importante estructurar sus clases en fragmentos lógicos y, por lo tanto, los métodos estáticos son bastante útiles cuando necesitamos agregar un método bajo una clase simplemente porque pertenece lógicamente a la clase. En nuestro ejemplo, el método estático llamadoget_employee_legal_obligations_txtsimplemente devuelve una cadena que contiene las obligaciones legales de cada empleado de una empresa. Esta función, no interactúa con la clase en sí ni con ninguna instancia. Podría haberse colocado en un módulo auxiliar diferente, sin embargo, solo es relevante para esta clase y, por lo tanto, tenemos que colocarlo en la clase Empleado.

Se puede acceder a un método estático directamente desde la propia clase

print(Employee.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

o de una instancia de la clase:

employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

Referencias

0

staticmethod no tiene acceso a los atributos del objeto, de la clase o de las clases padre en la jerarquía de herencia. Se puede llamar a la clase directamente (sin crear un objeto).

classmethod no tiene acceso a los atributos del objeto. Sin embargo, puede acceder a los atributos de la clase y de las clases principales en la jerarquía de herencia. Se puede llamar a la clase directamente (sin crear un objeto). Si se llama al objeto, es lo mismo que el método normal que no accede self.<attribute(s)>y self.__class__.<attribute(s)>solo accede .

Creo que tenemos una clase con b=2, crearemos un objeto y lo restableceremos b=4en él. Staticmethod no puede acceder a nada del anterior. Classmethod .b==2solo puede acceder a través de cls.b. El método normal puede acceder a ambos: .b==4via self.by .b==2via self.__class__.b.

Podríamos seguir el estilo KISS (manténgalo simple, estúpido): no use métodos estáticos y métodos de clase, no use clases sin instanciarlas, acceda solo a los atributos del objeto self.attribute(s). Hay lenguajes donde la POO se implementa de esa manera y creo que no es mala idea. :)

2
  • Una cosa más importante para los métodos de clase: si modifica un atributo en el método de clase, todos los objetos existentes de esta clase que no establezcan explícitamente este atributo tendrán el valor modificado.
    mirek
    2 mar.20 a las 16:14
  • la primera oración no es del todo cierta. Un método estático puede acceder a los miembros de la clase pública especificando el nombre de la clase, al igual que cualquier persona que llama
    joel
    13/08/20 a las 21:18
0

Python viene con varios decoradores integrados. Los tres grandes son:

@classmethod
@staticmethod
@property

El decorador @classmethod puede ser llamado con una instancia de una clase o directamente por la propia clase como su primer argumento.

@staticmethod es una forma de poner una función en una clase (porque lógicamente pertenece allí), al tiempo que indica que no requiere acceso a la clase.

Consideremos la siguiente clase:

class DecoratorTest(object):

    def __init__(self):
        pass

    def doubler(self, x):
        print("running doubler")
        return x*2

    @classmethod
    def class_doubler(klass, x):
        print("running doubler: %s" % klass)
        return x*2

    @staticmethod
    def static_doubler(x):
        print("running quad")
        return x*2

decor = DecoratorTest()

Vamos a ver cómo funciona:

print(decor.doubler(5))
# running doubler
# 10

print(decor.class_doubler(5))
# running doubler: <class '__main__.DecoratorTest'> 
# 10
print(DecoratorTest.class_doubler(5))
# running doubler: <class '__main__.DecoratorTest'> 
# 10

print(DecoratorTest.static_doubler(5))
# running doubler 
# 10
print(decor.static_doubler(5))
# running doubler 
# 10

print(decor.doubler)
# <bound method DecoratorTest.doubler of <__main__.DecoratorTest object at 0x7f90e74fd150>> 
print(decor.class_doubler)
# <bound method DecoratorTest.class_doubler of <class '__main__.DecoratorTest'>> 
print(decor.static_doubler)
# <function DecoratorTest.static_doubler at 0x7f90e7447440>