Cuando intento usar un método estático desde dentro del cuerpo de la clase, y definir el método estático usando la staticmethod
función incorporada como decorador, así:
class Klass(object):
@staticmethod # use as decorator
def _stat_func():
return 42
_ANS = _stat_func() # call the staticmethod
def method(self):
ret = Klass._stat_func() + Klass._ANS
return ret
Obtuve el siguiente error:
Traceback (most recent call last):<br>
File "call_staticmethod.py", line 1, in <module>
class Klass(object):
File "call_staticmethod.py", line 7, in Klass
_ANS = _stat_func()
TypeError: 'staticmethod' object is not callable
Entiendo por qué sucede esto (enlace del descriptor) , y puedo _stat_func()
solucionarlo convirtiéndolo manualmente en un método estático después de su último uso, así:
class Klass(object):
def _stat_func():
return 42
_ANS = _stat_func() # use the non-staticmethod version
_stat_func = staticmethod(_stat_func) # convert function to a static method
def method(self):
ret = Klass._stat_func() + Klass._ANS
return ret
Entonces mi pregunta es:
¿Hay mejores formas, como en limpia o más "pitón", de lograr esto?
python
decorator
static-methods
Martineau
fuente
fuente
staticmethod
en absoluto. Por lo general, son más útiles como funciones de nivel de módulo, en cuyo caso su problema no es un problema.classmethod
, por otro lado ...Respuestas:
staticmethod
Aparentemente, los objetos tienen un__func__
atributo que almacena la función original sin procesar (tiene sentido que tuvieran que hacerlo). Entonces esto funcionará:Por otro lado, aunque sospechaba que un objeto de método estático tenía algún tipo de atributo que almacenaba la función original, no tenía idea de los detalles. En el espíritu de enseñar a alguien a pescar en lugar de darle un pez, esto es lo que hice para investigar y descubrirlo (un C&P de mi sesión de Python):
Tipos similares de excavación en una sesión interactiva (
dir
es muy útil) a menudo pueden resolver este tipo de preguntas muy rápidamente.fuente
__func__
es solo otro nombreim_func
y se agregó a Py 2.6 para la compatibilidad con versiones anteriores de Python 3.__func__
atributo de un método estático le proporciona una referencia a la función original, exactamente como si nunca hubiera utilizado elstaticmethod
decorador. Entonces, si su función requiere argumentos, deberá pasarlos cuando llame__func__
. El mensaje de error parece que no le ha dado ningún argumento. Sistat_func
en el ejemplo de esta publicación tomara dos argumentos, usaría_ANS = stat_func.__func__(arg1, arg2)
stat_func
sí misma es una variable). ¿Quiso decir que no puede usar atributos de instancia de la clase que está definiendo? Eso es cierto, pero no sorprende; ¡No tenemos una instancia de la clase, ya que todavía estamos definiendo la clase! Pero de todos modos, me refería a ellos como sustitutos de cualquier argumento que quisieras pasar; podrías usar literales allí.Esta es la forma en que prefiero:
Prefiero esta solución
Klass.stat_func
, debido al principio DRY . Me recuerda la razón porsuper()
la cual hay una nueva en Python 3 :)Pero estoy de acuerdo con los demás, por lo general, la mejor opción es definir una función de nivel de módulo.
Por ejemplo, con la
@staticmethod
función, la recursión puede no verse muy bien (necesitaría romper el principio DRY llamando alKlass.stat_func
interiorKlass.stat_func
). Eso es porque no tiene referencia alself
método estático interno. Con la función de nivel de módulo, todo se verá bien.fuente
self.__class__.stat_func()
métodos regulares tiene ventajas (DRY y todo eso) sobre el usoKlass.stat_func()
, ese no fue realmente el tema de mi pregunta; de hecho, evité usar el primero para no nublar el problema de la inconsistencia.self.__class__
es mejor porque si una subclase anulastat_func
, entoncesSubclass.method
llamará a la subclasestat_func
. Pero para ser completamente honesto, en esa situación es mucho mejor usar un método real en lugar de uno estático.¿Qué hay de inyectar el atributo de clase después de la definición de clase?
fuente
Esto se debe a que el método estático es un descriptor y requiere una búsqueda de atributos a nivel de clase para ejercer el protocolo del descriptor y obtener el verdadero invocable.
Del código fuente:
Pero no directamente desde dentro de la clase mientras se está definiendo.
Pero como mencionó un comentarista, este no es realmente un diseño "pitónico" en absoluto. Simplemente use una función de nivel de módulo en su lugar.
fuente
¿Qué hay de esta solución? No se basa en el conocimiento de la
@staticmethod
implementación del decorador. La clase interna StaticMethod juega como un contenedor de funciones de inicialización estática.fuente
__func__
porque ahora está documentado oficialmente (consulte la sección Otros cambios de idioma de las novedades de Python 2.7 y su referencia al problema 5982 ). Su solución es aún más portátil, ya que probablemente también funcionaría en versiones de Python anteriores a 2.6 (cuando__func__
se introdujo por primera vez como sinónimo deim_func
).