variable property method may classes python static-methods

python - property - Función de módulo vs método estático vs método de clase vs no decoradores: ¿Qué idioma es más pitónico?



python static variable (4)

Esto no es realmente una respuesta, sino un comentario largo:

Aún más desconcertante es que este código:

  class A: def foo(x): print(x) A.foo(5)

Falla como se esperaba en Python 2.7.3 pero funciona bien en 3.2.3 (aunque no puede llamar al método en una instancia de A, solo en la clase).

Trataré de explicar lo que pasa aquí.

Esto es, estrictamente hablando, un abuso del protocolo del método de instancia "normal".

Lo que define aquí es un método, pero con el primer (y único) parámetro que no se llama self , pero x . Por supuesto, puedes llamar al método en una instancia de A , pero tendrás que llamarlo así:

A().foo()

o

a = A() a.foo()

así se le da la instancia a la función como primer argumento.

La posibilidad de llamar a métodos regulares a través de la clase siempre ha estado allí y funciona por

a = A() A.foo(a)

Aquí, al llamar al método de la clase en lugar de a la instancia, no recibe su primer parámetro automáticamente, pero tendrá que proporcionarlo.

Mientras esta sea una instancia de A , todo está bien. Darle algo más es un abuso del protocolo a la OMI y, por lo tanto, la diferencia entre Py2 y Py3:

En Py2, A.foo se transforma en un método A.foo y, por lo tanto, requiere que su primer argumento sea una instancia de la clase en la que "vive". Llamarlo con otra cosa fallará.

En Py3, esta comprobación se ha eliminado y A.foo es solo el objeto de función original. Así que puedes llamarlo con todo como primer argumento, pero no lo haría. El primer parámetro de un método siempre debe llamarse self y tener la semántica de self .

Soy un desarrollador de Java que ha jugado con Python de forma intermitente. Recientemente me topé con este artículo que menciona errores comunes que cometen los programadores de Java cuando seleccionan Python. El primero me llamó la atención:

Un método estático en Java no se traduce a un método de clase Python. Oh, claro, da como resultado más o menos el mismo efecto, pero el objetivo de un método de clase en realidad es hacer algo que generalmente no es posible en Java (como heredar un constructor no predeterminado). La traducción idiomática de un método estático de Java es generalmente una función de nivel de módulo, no un método de clase o un método estático. (Y los campos finales estáticos deberían traducirse en constantes de nivel de módulo).

Esto no es un gran problema de rendimiento, pero un programador de Python que tiene que trabajar con código de lenguaje Java como este se irritará bastante al escribir Foo.Foo.someMethod cuando debería ser Foo.someFunction. Pero tenga en cuenta que llamar a un método de clase implica una asignación de memoria adicional que no implica llamar a un método o función estática.

Ah, y todas esas cadenas de atributos Foo.Bar.Baz no vienen gratis, tampoco. En Java, el compilador busca esos nombres de puntos, por lo que en tiempo de ejecución realmente no importa cuántos de ellos tengas. En Python, las búsquedas se producen en tiempo de ejecución, por lo que cada punto cuenta. (Recuerde que en Python, "Plano es mejor que anidado", aunque está más relacionado con "Recuentos de legibilidad" y "Simple es mejor que complejo" que con el rendimiento).

Esto me pareció un poco extraño porque la documentación para el staticmethod dice:

Los métodos estáticos en Python son similares a los encontrados en Java o C ++. También vea classmethod () para una variante que es útil para crear constructores de clase alternativos.

Aún más desconcertante es que este código:

class A: def foo(x): print(x) A.foo(5)

Falla como se esperaba en Python 2.7.3 pero funciona bien en 3.2.3 (aunque no puede llamar al método en una instancia de A, solo en la clase).

Así que hay tres formas de implementar métodos estáticos (cuatro si se cuenta usando el método de clase), cada uno con diferencias sutiles, una de ellas aparentemente no documentada. Esto parece estar en desacuerdo con el mantra de Python de que debería haber uno, y preferiblemente solo una, una forma obvia de hacerlo. ¿Qué idioma es el más pitónico? ¿Cuáles son los pros y los contras de cada uno?

Esto es lo que entiendo hasta ahora:

Función del módulo:

  • Evita el problema Foo.Foo.f ()
  • Contamina el espacio de nombres del módulo más que las alternativas.
  • Sin herencia

método estático

  • Mantiene las funciones relacionadas con la clase dentro de la clase y fuera del espacio de nombres del módulo.
  • Permite llamar a la función en instancias de la clase.
  • Las subclases pueden anular el método.

método de clase

  • Idéntico al método estático, pero también pasa la clase como primer argumento.

Método regular (solo Python 3) :

  • Idéntico al método estático, pero no puede llamar al método en instancias de la clase.

¿Estoy pensando demasiado en esto? ¿Esto no es un problema? ¡Por favor ayuda!


Gran respuesta de , pero cambiaría ''Si no necesita acceso a la clase o la instancia, haga que sea una función'' para:

''Si no necesita acceso a la clase o la instancia ... pero está relacionado temáticamente con la clase (ejemplo típico: funciones de ayuda y funciones de conversión usadas por otros métodos de clase o usadas por constructores alternativos), entonces use staticmethod

de lo contrario haz que sea una función de módulo


La forma más directa de pensar en ello es pensar en términos de qué tipo de objeto necesita el método para realizar su trabajo. Si su método necesita acceso a una instancia, conviértalo en un método regular. Si necesita acceso a la clase, conviértalo en un método de clase. Si no necesita acceso a la clase o la instancia, conviértala en una función. Rara vez hay una necesidad de hacer que algo sea un método estático, pero si desea que una función se "agrupe" con una clase (por ejemplo, para que pueda ser anulada) aunque no necesite acceder a la clase, supongo Podrías convertirlo en un método estático.

Me gustaría agregar que poner funciones en el nivel del módulo no "contamina" el espacio de nombres. Si las funciones están destinadas a ser utilizadas, no están contaminando el espacio de nombres, lo están usando como se debe usar. Las funciones son objetos legítimos en un módulo, al igual que las clases o cualquier otra cosa. No hay razón para ocultar una función en una clase si no tiene ninguna razón para estar allí.


La mejor respuesta depende de cómo se va a utilizar la función. En mi caso, escribo paquetes de aplicaciones que se utilizarán en los cuadernos Jupyter. Mi objetivo principal es facilitar las cosas para el usuario.

La principal ventaja de las definiciones de funciones es que el usuario puede importar su archivo de definición utilizando la palabra clave "como". Esto permite al usuario llamar a las funciones de la misma manera que llamarían a una función en numpy o matplotlib.

Una de las desventajas de Python es que los nombres no pueden protegerse contra la asignación adicional. Sin embargo, si aparece "importar número como np" en la parte superior del cuaderno, es un indicio sólido de que "np" no debe usarse como un nombre de variable común. Obviamente, puede lograr lo mismo con los nombres de clase, pero la familiaridad del usuario cuenta mucho.

Dentro de los paquetes, sin embargo, prefiero usar métodos estáticos. Mi arquitectura de software está orientada a objetos, y escribo con Eclipse, que uso para múltiples idiomas de destino. Es conveniente abrir el archivo de origen y ver la definición de clase en el nivel superior, las definiciones de método sangradas en un nivel, etc. La audiencia para el código en este nivel es principalmente otros analistas y desarrolladores, por lo que es mejor evitar modismos específicos del idioma.

No tengo mucha confianza en la administración del espacio de nombres de Python, especialmente cuando uso patrones de diseño donde (por ejemplo) un objeto pasa una referencia a sí mismo para que el objeto llamado pueda llamar a un método definido en el llamante. Así que trato de no forzarlo demasiado. Utilizo muchos nombres completos y variables de instancia explícitas (con uno mismo ) donde en otros idiomas puedo contar con que el intérprete o el compilador gestionen el alcance más de cerca. Es más fácil hacer esto con clases y métodos estáticos, por lo que creo que son la mejor opción para paquetes complejos donde la abstracción y la ocultación de información son más útiles.