funciones - llamar metodo de otra clase python
¿Cuál es el propósito de los métodos de clase? (15)
¿Honestamente? Nunca he encontrado un uso para el método estático o el método de clase. Todavía no he visto una operación que no se pueda hacer usando una función global o un método de instancia.
Sería diferente si Python utilizara miembros privados y protegidos más como Java. En Java, necesito un método estático para poder acceder a los miembros privados de una instancia para hacer cosas. En Python, eso rara vez es necesario.
Por lo general, veo que la gente usa métodos estáticos y métodos de clase cuando lo único que realmente necesitan hacer es usar mejor los espacios de nombres de nivel de módulo de Python.
Me estoy enseñando a Python y mi lección más reciente fue que Python no es Java , por lo que acabo de dedicar un tiempo a convertir todos mis métodos de clase en funciones.
Ahora me doy cuenta de que no necesito usar métodos de clase para lo que haría con métodos static
en Java, pero ahora no estoy seguro de cuándo los usaría. Todos los consejos que puedo encontrar acerca de los métodos de la Clase Python están en línea con los novatos como yo, deberían evitarlos, y la documentación estándar es más opaca cuando se los discute.
¿Alguien tiene un buen ejemplo del uso de un método de clase en Python o al menos alguien puede decirme cuándo se pueden usar los métodos de clase con sensatez?
Constructores alternativos son el ejemplo clásico.
Creo que la respuesta más clara es la de AmanKow . Se reduce a cómo quieres organizar tu código. Puede escribir todo como funciones de nivel de módulo que están envueltas en el espacio de nombres del módulo, es decir
module.py (file 1)
---------
def f1() : pass
def f2() : pass
def f3() : pass
usage.py (file 2)
--------
from module import *
f1()
f2()
f3()
def f4():pass
def f5():pass
usage1.py (file 3)
-------------------
from usage import f4,f5
f4()
f5()
El código de procedimiento anterior no está bien organizado, como puede ver, después de solo 3 módulos se confunde, ¿qué hace cada método? Puede usar nombres largos y descriptivos para funciones (como en java) pero aún así su código se vuelve inmanejable muy rápido.
La forma orientada a objetos es dividir su código en bloques manejables, es decir, las clases y los objetos y las funciones pueden asociarse con instancias de objetos o con clases.
Con las funciones de clase, obtiene otro nivel de división en su código en comparación con las funciones de nivel de módulo. Por lo tanto, puede agrupar funciones relacionadas dentro de una clase para hacerlas más específicas a una tarea que asignó a esa clase. Por ejemplo, puede crear una clase de utilidad de archivo:
class FileUtil ():
def copy(source,dest):pass
def move(source,dest):pass
def copyDir(source,dest):pass
def moveDir(source,dest):pass
//usage
FileUtil.copy("1.txt","2.txt")
FileUtil.moveDir("dir1","dir2")
De esta manera es más flexible y más fácil de mantener, agrupa las funciones y es más obvio lo que hace cada función. También evita conflictos de nombres, por ejemplo, la copia de la función puede existir en otro módulo importado (por ejemplo, copia de red) que utiliza en su código, por lo que cuando usa el nombre completo FileUtil.copy (), elimina el problema y ambas funciones de copia Se pueden usar lado a lado.
Cuando un usuario inicia sesión en mi sitio web, se crea una instancia del objeto Usuario () desde el nombre de usuario y la contraseña.
Si necesito un objeto de usuario sin que el usuario esté allí para iniciar sesión (por ejemplo, un usuario administrador puede querer eliminar la cuenta de otro usuario, por lo que debo crear una instancia de ese usuario y llamar a su método de eliminación):
Tengo métodos de clase para agarrar el objeto de usuario.
class User():
#lots of code
#...
# more code
@classmethod
def get_by_username(cls, username):
return cls.query(cls.username == username).get()
@classmethod
def get_by_auth_id(cls, auth_id):
return cls.query(cls.auth_id == auth_id).get()
Este es un tema interesante. Mi opinión es que el método de clase de Python funciona como un singleton en lugar de una fábrica (que devuelve una instancia de una clase producida). La razón por la que es un singleton es que hay un objeto común que se produce (el diccionario) pero solo una vez para la clase pero compartido por todas las instancias.
Para ilustrar esto aquí hay un ejemplo. Tenga en cuenta que todas las instancias tienen una referencia al diccionario único. Esto no es un patrón de fábrica como lo entiendo. Esto es probablemente muy exclusivo de python.
class M():
@classmethod
def m(cls, arg):
print "arg was", getattr(cls, "arg" , None),
cls.arg = arg
print "arg is" , cls.arg
M.m(1) # prints arg was None arg is 1
M.m(2) # prints arg was 1 arg is 2
m1 = M()
m2 = M()
m1.m(3) # prints arg was 2 arg is 3
m2.m(4) # prints arg was 3 arg is 4 << this breaks the factory pattern theory.
M.m(5) # prints arg was 4 arg is 5
Hace poco quería una clase de registro muy liviana que pudiera generar cantidades variables de salida dependiendo del nivel de registro que se podría establecer mediante programación. Pero no quería crear una instancia de la clase cada vez que quería enviar un mensaje de depuración o un error o advertencia. Pero también quería encapsular el funcionamiento de esta instalación de registro y hacerlo reutilizable sin la declaración de ningún global.
Así que utilicé variables de clase y el decorador @classmethod
para lograr esto.
Con mi clase de registro simple, podría hacer lo siguiente:
Logger._level = Logger.DEBUG
Luego, en mi código, si quería escupir un montón de información de depuración, simplemente tenía que codificar
Logger.debug( "this is some annoying message I only want to see while debugging" )
Los errores pueden ser eliminados con
Logger.error( "Wow, something really awful happened." )
En el entorno de "producción", puedo especificar
Logger._level = Logger.ERROR
y ahora, solo se mostrará el mensaje de error. El mensaje de depuración no se imprimirá.
Aquí está mi clase:
class Logger :
'''''' Handles logging of debugging and error messages. ''''''
DEBUG = 5
INFO = 4
WARN = 3
ERROR = 2
FATAL = 1
_level = DEBUG
def __init__( self ) :
Logger._level = Logger.DEBUG
@classmethod
def isLevel( cls, level ) :
return cls._level >= level
@classmethod
def debug( cls, message ) :
if cls.isLevel( Logger.DEBUG ) :
print "DEBUG: " + message
@classmethod
def info( cls, message ) :
if cls.isLevel( Logger.INFO ) :
print "INFO : " + message
@classmethod
def warn( cls, message ) :
if cls.isLevel( Logger.WARN ) :
print "WARN : " + message
@classmethod
def error( cls, message ) :
if cls.isLevel( Logger.ERROR ) :
print "ERROR: " + message
@classmethod
def fatal( cls, message ) :
if cls.isLevel( Logger.FATAL ) :
print "FATAL: " + message
Y algún código que lo prueba un poco:
def logAll() :
Logger.debug( "This is a Debug message." )
Logger.info ( "This is a Info message." )
Logger.warn ( "This is a Warn message." )
Logger.error( "This is a Error message." )
Logger.fatal( "This is a Fatal message." )
if __name__ == ''__main__'' :
print "Should see all DEBUG and higher"
Logger._level = Logger.DEBUG
logAll()
print "Should see all ERROR and higher"
Logger._level = Logger.ERROR
logAll()
Le permite escribir métodos de clase genéricos que puede usar con cualquier clase compatible.
Por ejemplo:
@classmethod
def get_name(cls):
print cls.name
class C:
name = "tester"
C.get_name = get_name
#call it:
C.get_name()
Si no usa @classmethod
, puede hacerlo con la palabra clave self pero necesita una instancia de Class:
def get_name(self):
print self.name
class C:
name = "tester"
C.get_name = get_name
#call it:
C().get_name() #<-note the its an instance of class C
Lo que me llamó la atención , proveniente de Ruby, es que el llamado método de clase y el llamado método de instancia son solo una función con significado semántico aplicado a su primer parámetro, que se pasa silenciosamente cuando se llama a la función como un método de un objeto (es decir, obj.meth()
).
Normalmente ese objeto debe ser una instancia, pero el decorador del método @classmethod
cambia las reglas para pasar una clase. Puede llamar a un método de clase en una instancia (es solo una función) - el primer argumento será su clase.
Debido a que es solo una función , solo se puede declarar una vez en cualquier ámbito dado (es decir class
definición de class
). Si sigue, por lo tanto, como sorpresa para un Rubyist, no puede tener un método de clase y un método de instancia con el mismo nombre .
Considera esto:
class Foo():
def foo(x):
print(x)
Puedes llamar a foo
en una instancia
Foo().foo()
<__main__.Foo instance at 0x7f4dd3e3bc20>
Pero no en una clase:
Foo.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Foo instance as first argument (got nothing instead)
Ahora agregue @classmethod
:
class Foo():
@classmethod
def foo(x):
print(x)
Llamar a una instancia ahora pasa su clase:
Foo().foo()
__main__.Foo
al igual que llamar a una clase:
Foo.foo()
__main__.Foo
Solo la convención dicta que usemos self
para ese primer argumento en un método de instancia y cls
en un método de clase. No utilicé aquí para ilustrar que es solo un argumento. En Ruby, el self
es una palabra clave.
Contraste con Ruby:
class Foo
def foo()
puts "instance method #{self}"
end
def self.foo()
puts "class method #{self}"
end
end
Foo.foo()
class method Foo
Foo.new.foo()
instance method #<Foo:0x000000020fe018>
El método de clase Python es solo una función decorada y puedes usar las mismas técnicas para crear tus propios decoradores . Un método decorado envuelve el método real (en el caso de @classmethod
pasa el argumento de clase adicional). El método subyacente sigue ahí, oculto pero todavía accesible .
nota al pie: Escribí esto después de que un conflicto de nombres entre una clase y un método de instancia despertó mi curiosidad. Estoy lejos de ser un experto en Python y me gustaría recibir comentarios si algo de esto está mal.
Los métodos de clase proporcionan un "azúcar semántica" (no sé si este término es ampliamente utilizado) o "conveniencia semántica".
Ejemplo: tienes un conjunto de clases que representan objetos. Es posible que desee tener el método de clase all()
o find()
para escribir User.all()
o User.find(firstname=''Guido'')
. Eso podría hacerse usando funciones de nivel de módulo, por supuesto ...
Los métodos de clase son para cuando necesita tener métodos que no sean específicos de una instancia en particular, pero que aún involucren a la clase de alguna manera. Lo más interesante de ellos es que pueden ser reemplazados por subclases, algo que simplemente no es posible en los métodos estáticos de Java o en las funciones de nivel de módulo de Python.
Si tiene una clase MyClass
y una función de nivel de módulo que opera en MyClass (fábrica, talón de inyección de dependencia, etc.), classmethod
un classmethod
. Entonces estará disponible para las subclases.
Los métodos de fábrica (constructores alternativos) son de hecho un ejemplo clásico de métodos de clase.
Básicamente, los métodos de clase son adecuados en cualquier momento en que le gustaría tener un método que naturalmente se ajuste al espacio de nombres de la clase, pero que no esté asociado con una instancia particular de la clase.
Como ejemplo, en el excelente módulo unipath :
Directorio actual
-
Path.cwd()
- Devuelve el directorio actual actual; por ejemplo,
Path("/tmp/my_temp_dir")
. Este es un método de clase.
- Devuelve el directorio actual actual; por ejemplo,
-
.chdir()
- Hazte el directorio actual.
Como el directorio actual es de todo el proceso, el método cwd
no tiene una instancia particular con la que deba asociarse. Sin embargo, cambiar el cwd
al directorio de una instancia de Path
dada debería ser un método de instancia.
Hmmm ... como Path.cwd()
devuelve una instancia de Path
, supongo que podría considerarse un método de fábrica ...
Me hacía la misma pregunta pocas veces. Y a pesar de que los chicos aquí intentaron explicarlo, en mi humilde opinión, la mejor respuesta (y la más simple) que he encontrado es la description del método de clase en la documentación de Python.
También hay referencia al método estático. Y en caso de que alguien ya conozca los métodos de ejemplo (que asumo), esta respuesta podría ser la pieza final para ponerlo todo junto ...
Puede encontrar más detalles sobre este tema en la documentación: La jerarquía de tipos estándar (desplácese hacia abajo hasta la sección Métodos de instancia )
Piénselo de esta manera: los métodos normales son útiles para ocultar los detalles del envío: puede escribir myobj.foo()
sin preocuparse de si el método foo()
es implementado por la myobj
objeto myobj
o una de sus clases primarias. Los métodos de clase son exactamente análogos a esto, pero con el objeto de clase en su lugar: le permiten llamar a MyClass.foo()
sin tener que preocuparse de si foo()
está implementado especialmente por MyClass
porque necesitaba su propia versión especializada, o si es dejando que su clase padre se encargue de la llamada.
Los métodos de clase son esenciales cuando realiza una configuración o cálculo que precede a la creación de una instancia real, porque hasta que no exista la instancia, obviamente no puede usar la instancia como el punto de despacho para sus llamadas de método. Un buen ejemplo se puede ver en el código fuente de SQLAlchemy; Eche un vistazo al método de clase dbapi()
en el siguiente enlace:
Puede ver que el método dbapi()
, que utiliza un backend de base de datos para importar la biblioteca de base de datos específica del proveedor que necesita bajo demanda, es un método de clase porque debe ejecutarse antes de que se creen las instancias de una conexión de base de datos en particular, pero que no puede ser una función simple o una función estática, porque quieren que sea capaz de llamar a otros métodos de apoyo que podrían necesitar escribirse más específicamente en subclases que en su clase principal. Y si envía a una función o clase estática, entonces "olvida" y pierde el conocimiento sobre qué clase está realizando la inicialización.
Solía trabajar con PHP y recientemente me preguntaba, ¿qué está pasando con este método de clase? El manual de Python es muy técnico y muy corto en palabras, por lo que no ayudará a entender esa característica. Estaba buscando en Google y encontré la respuesta -> http://code.anjanesh.net/2007/12/python-classmethods.html .
Si eres perezoso haz clic en él. Mi explicación es más corta y más abajo. :)
en PHP (tal vez no todos ustedes saben PHP, pero este lenguaje es tan sencillo que todos deberían entender de lo que estoy hablando) tenemos variables estáticas como esta:
class A
{
static protected $inner_var = null;
static public function echoInnerVar()
{
echo self::$inner_var."/n";
}
static public function setInnerVar($v)
{
self::$inner_var = $v;
}
}
class B extends A
{
}
A::setInnerVar(10);
B::setInnerVar(20);
A::echoInnerVar();
B::echoInnerVar();
La salida será en ambos casos 20.
Sin embargo, en Python podemos agregar @classmethod decorator y, por lo tanto, es posible tener una salida 10 y 20 respectivamente. Ejemplo:
class A(object):
inner_var = 0
@classmethod
def setInnerVar(cls, value):
cls.inner_var = value
@classmethod
def echoInnerVar(cls):
print cls.inner_var
class B(A):
pass
A.setInnerVar(10)
B.setInnerVar(20)
A.echoInnerVar()
B.echoInnerVar()
Inteligente, ¿no?
Una clase define un conjunto de instancias, por supuesto. Y los métodos de un trabajo de clase en las instancias individuales. Los métodos de clase (y las variables) son un lugar para colgar otra información relacionada con el conjunto de instancias en general.
Por ejemplo, si su clase define el conjunto de alumnos, es posible que desee variables de clase o métodos que definan elementos como el conjunto de calificaciones de los cuales los alumnos pueden ser miembros.
También puede usar métodos de clase para definir herramientas para trabajar en todo el conjunto. Por ejemplo, Student.all_of_em () puede devolver todos los alumnos conocidos. Obviamente, si su conjunto de instancias tiene más estructura que solo un conjunto, puede proporcionar métodos de clase para conocer esa estructura. Students.all_of_em (grado = ''juniors'')
Las técnicas como esta tienden a conducir a almacenar miembros del conjunto de instancias en estructuras de datos que están enraizadas en variables de clase. Tienes que tener cuidado para evitar frustrar la recolección de basura entonces.