¿Qué son los "métodos de clase" y los "métodos de instancia" en Python?

37

Ha habido una discusión en el chat relacionada con una pregunta (la pregunta en sí misma es irrelevante para esta), que ha revelado que quizás no conozca Python en absoluto.

En mi opinión, aunque la terminología difiere entre idiomas, generalmente podemos clasificar las funciones como:

  • funciones [gratis]
  • métodos estáticos / funciones miembro estáticas
  • métodos no estáticos / funciones miembro no estáticas

Aparentemente, en Python hay otro tipo de función que no encaja en las categorías anteriores, una que es un método pero "no conoce su clase".

¿Qué son los "métodos de clase" y los "métodos de instancia" en Python?

La ligereza corre con Mónica
fuente
1
Si alguien está interesado en la discusión del chat, comience aquí: chat.stackexchange.com/transcript/message/26479002#26479002
yannis
1
En mi opinión, un sitio web diferente de stackexchange proporciona una mejor respuesta: stackoverflow.com/questions/136097/…
Trevor Boyd Smith el
FYI, el enlace se proporciona en la parte inferior de lo aceptado ... pero creo que muchas personas perderán el enlace o nunca lo accederán, así que lo copié aquí.
Trevor Boyd Smith

Respuestas:

49

La respuesta corta

  • un método de instancia conoce su instancia (y de eso, su clase)
  • un método de clase conoce su clase
  • un método estático no conoce su clase o instancia

La respuesta larga

Métodos de clase

Un método de clase es uno que pertenece a la clase como un todo. No requiere una instancia. En cambio, la clase se enviará automáticamente como primer argumento. Se declara un método de clase con el @classmethoddecorador.

Por ejemplo:

class Foo(object):
    @classmethod
    def hello(cls):
        print("hello from %s" % cls.__name__)
Foo.hello()
-> "Hello from Foo"
Foo().hello()
-> "Hello from Foo"

Métodos de instancia

Por otro lado, un método de instancia requiere una instancia para llamarlo y no requiere decorador. Este es, con mucho, el tipo de método más común.

class Foo(object):
    def hello(self):
        print("hello from %s" % self.__class__.__name__)
Foo.hello()
-> TypeError: hello() missing 1 required positional argument: 'self'

(nota: lo anterior es con python3; con python2 obtendrá un error ligeramente diferente)

Métodos estáticos

Un método estático es similar a un método de clase, pero no obtendrá el objeto de clase como un parámetro automático. Se crea utilizando el @staticmethoddecorador.

class Foo(object):
    @staticmethod
    def hello(cls):
        print("hello from %s" % cls.__name__)

Foo.hello()
-> TypeError: hello() missing 1 required positional argument: 'cls'

Enlaces de documentación

Aquí hay enlaces a la documentación relevante de python3:

La documentación del modelo de datos tiene esto que decir sobre la diferencia entre los métodos de clase y los métodos estáticos:

Objetos de método estático Los objetos de método estático proporcionan una forma de derrotar la transformación de los objetos de función en objetos de método descritos anteriormente. Un objeto de método estático es un contenedor alrededor de cualquier otro objeto, generalmente un objeto de método definido por el usuario. Cuando se recupera un objeto de método estático de una clase o una instancia de clase, el objeto realmente devuelto es el objeto envuelto, que no está sujeto a ninguna transformación adicional. Los objetos del método estático no son invocables, aunque los objetos que envuelven generalmente lo son. Los objetos de método estático son creados por el constructor staticmethod () incorporado.

Objetos de método de clase Un objeto de método de clase, como un objeto de método estático, es un contenedor alrededor de otro objeto que altera la forma en que ese objeto se recupera de las clases y las instancias de clase. El comportamiento de los objetos de método de clase en dicha recuperación se describe anteriormente, en "Métodos definidos por el usuario". Los objetos de método de clase son creados por el constructor de classmethod () incorporado.

Preguntas relacionadas

Bryan Oakley
fuente
44
@LightnessRacesinOrbit: no creo que los métodos estáticos se usen con mucha frecuencia en Python. Usando mi sistema como una muestra aleatoria, cuando busco en todos los paquetes que he instalado, solo alrededor del 1% de todos los métodos son métodos estáticos (que francamente fue más de lo que esperaba).
Bryan Oakley
1
Los "métodos estáticos" suelen ser un olor a código y, por ejemplo, no se recomiendan en la guía de estilo Gogole Python.
9000
3
Creo que la respuesta sería mejor si llamara la atención sobre las subclases, que es donde los métodos de clase obtienen alguna utilidad.
Winston Ewert
1
@ user2357112: simplemente conté todas las instancias de "^ def(", luego conté todas las instancias de @staticmethod. Muy poco científico, pero probablemente estaba dentro del estadio.
Bryan Oakley
2
@Kashyap: el TL; DR se aplica al primer párrafo. Creo que funciona mejor en la parte superior que en la inferior.
Bryan Oakley
8

¿Qué son los "métodos de clase" y los "métodos de instancia" en Python?

  • Un "método de instancia" utiliza la información contenida en la instancia para determinar qué valor devolver (o qué efecto secundario hacer). Estos son muy comunes.

  • Un "método de clase" usa información sobre la clase (y no una instancia de esa clase) para afectar lo que hace (se usan generalmente para crear nuevas instancias como constructores alternativos, y por lo tanto no son increíblemente comunes).

  • Un "método estático" no utiliza ninguna información sobre la clase o instancia para calcular lo que hace. Por lo general, es solo en la clase por conveniencia. (Como tal, estos tampoco son muy comunes).

Una función de X

Recuerde la clase de matemáticas, "y es una función de x f(x),?" Apliquemos eso en el código:

y = function(x)

Implícito en lo anterior es que, dado que xpuede cambiar, ypuede cambiar cuando xcambia. Esto es lo que significa cuando decimos que " yes una función de x"

¿Qué será ycuando zsea 1? 2? 'FooBarBaz'?

yno es una función de z, por lo que zpuede ser cualquier cosa y no afectar el resultado de la función suponiendo que nuestra functiones una función pura. (Si accede zcomo una variable global, entonces no es una función pura, esto es lo que se entiende por pureza funcional).

Tenga en cuenta lo anterior mientras lee las siguientes descripciones:

Métodos de instancia

Un método de instancia es una función que es función de una instancia . La función acepta la instancia implícitamente como un argumento, y la instancia la utiliza para determinar el resultado de la función.

Un ejemplo incorporado de un método de instancia es str.lower:

>>> 'ABC'.lower()
'abc'

str.lower se llama en la instancia de la cadena y usa la información contenida en la instancia para determinar qué nueva cadena devolver.

Métodos de clase:

Recuerde, en Python, todo es un objeto. Eso significa que la clase es un objeto y se puede pasar como argumento a una función.

Un método de clase es una función que es una función de la clase . Acepta la clase como un argumento para ello.

Un ejemplo incorporado es dict.fromkeys:

>>> dict.fromkeys('ABC')
{'C': None, 'B': None, 'A': None}

La función conoce implícitamente su propia clase, la función utiliza la clase para afectar la salida de la función y crea una nueva de esa clase a partir del iterable. Un OrderedDict demuestra esto cuando se usa el mismo método:

>>> from collections import OrderedDict
>>> OrderedDict.fromkeys('ABC')
OrderedDict([('A', None), ('B', None), ('C', None)])

El método de clase utiliza información sobre la clase (y no una instancia de esa clase) para afectar qué tipo de clase devolver.

Métodos Estáticos

Usted menciona un método que "no conoce su clase"; este es un método estático en Python. Simplemente se adjunta por conveniencia al objeto de clase. Opcionalmente, podría ser una función separada en otro módulo, pero su firma de llamada sería la misma.

Un método estático no es función de la clase ni del objeto.

Un ejemplo incorporado de un método estático es str.maketrans de Python 3.

>>> str.maketrans('abc', 'bca')
{97: 98, 98: 99, 99: 97}

Dado un par de argumentos, crea un diccionario que no es una función de su clase.

Es conveniente porque strsiempre está disponible en el espacio de nombres global, por lo que puede usarlo fácilmente con la función de traducción:

>>> 'abracadabra'.translate(str.maketrans('abc', 'bca'))
'bcrbabdbcrb'

En Python 2, debe acceder desde el stringmódulo:

>>> 'abracadabra'.translate(str.maketrans('abc', 'bca'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'str' has no attribute 'maketrans'
>>> import string
>>> 'abracadabra'.translate(string.maketrans('abc', 'bca'))
'bcrbabdbcrb'

Ejemplo

class AClass(object):
    """In Python, a class may have several types of methods: 
    instance methods, class methods, and static methods
    """

    def an_instance_method(self, x, y, z=None):
        """this is a function of the instance of the object
        self is the object's instance
        """
        return self.a_class_method(x, y)

    @classmethod
    def a_class_method(cls, x, y, z=None):
        """this is a function of the class of the object
        cls is the object's class
        """
        return cls.a_static_method(x, y, z=z)

    @staticmethod
    def a_static_method(x, y, z=None):
        """this is neither a function of the instance or class to 
        which it is attached
        """
        return x, y, z

Vamos a instanciar:

>>> instance = AClass()

Ahora la instancia puede llamar a todos los métodos:

>>> instance.an_instance_method('x', 'y')
('x', 'y', None)
>>> instance.a_static_method('x', 'y')
('x', 'y', None)
>>> instance.a_class_method('x', 'y')
('x', 'y', None)

Pero la clase generalmente no tiene la intención de llamar al método de instancia, aunque se espera que llame a los demás:

>>> AClass.a_class_method('x', 'y')
('x', 'y', None)
>>> AClass.a_static_method('x', 'y')
('x', 'y', None)
>>> AClass.an_instance_method('x', 'y')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: an_instance_method() missing 1 required positional argument: 'y'

Tendría que pasar explícitamente la instancia para llamar al método de instancia:

>>> AClass.an_instance_method(instance, 'x', 'y')
('x', 'y', None)
Aaron Hall
fuente
"Este es un método estático en Python. Simplemente se adjunta por conveniencia al objeto de clase. Opcionalmente, podría ser una función separada en otro módulo, pero su firma de llamada sería la misma". Parece más confuso que conveniente. ¿Por qué asociar una función a una clase si su cuerpo no tiene noción de esa clase?
Lightness compite con Monica el
@LightnessRacesinOrbit he elaborado en cada punto
Aaron Hall
Personalmente, la forma en que siempre lo he visto es que un método de instancia es una operación en una instancia en particular, un método de clase es una especie de constructor (por conveniencia, nada parecido, MyClass(1,2,3)sino más bien como MyClass.get_special(1,2,3)), y un método estático como solo siendo una función normal que podría estar sola de todos modos.
Zizouz212
Sí, los métodos de clase se usan típicamente para constructores alternativos (vea mi ejemplo dict.fromkeys arriba, vea también el código fuente pandas.DataFrame), pero esa no es una característica definitoria. A veces simplemente necesito acceder a los datos almacenados a nivel de clase desde un método, sin acceder a la instancia. Si está llamando a type (self) (o peor, self .__ class__) y no está utilizando self directamente en un método de instancia, es una buena señal de que debería considerarlo como un método de clase. Como los métodos de clase no requieren instancias, los hace más fáciles de probar por unidad.
Aaron Hall
4
  • Un "método de instancia" es un método que pasa el objeto de instancia como su primer parámetro, que por convención se llama self. Este es el valor predeterminado.

  • Un "método estático" es un método sin el selfparámetro inicial . Esto se implementa con el decorador @staticmethod .

  • Un "método de clase" es un método en el que el parámetro inicial no es un objeto de instancia, sino el objeto de clase ( consulte esta parte de los documentos de Python para saber qué es un "objeto de clase"), que por convención se llama cls. Esto se implementa con el decorador @classmethod .

El uso típico del término "método estático" en C / C ++ / etc se aplica tanto a los métodos "estáticos" como a la "clase" de Python, ya que ambos tipos de métodos carecen de una referencia a la instancia. En C / C ++ / etc, los métodos normalmente tienen acceso implícito a todos los miembros / métodos que no son de instancia de la clase, por lo que probablemente esta distinción solo existe en lenguajes como Python / Ruby / etc.

Ixrec
fuente
¿Un método estático en C / C ++ tiene una referencia al objeto de clase? O simplemente "miembros" de la clase? ¿Pueden crear nuevos miembros de la clase? :)
Aaron Hall
3
@AaronHall En C / C ++, no existe un objeto de clase, y no necesita una referencia para llamar a métodos estáticos. Simplemente escriba Class::staticMethod()y listo (siempre y cuando no tenga prohibido llamarlo porque staticMethod()es privado o algo así).
Ixrec