¿Alguien podría explicarme el significado de @classmethod
y @staticmethod
en python? Necesito saber la diferencia y el significado.
Por lo que entiendo, @classmethod
le dice a una clase que es un método que debe ser heredado en subclases, o... algo. Sin embargo, ¿cuál es el punto de eso? ¿Por qué no simplemente definir el método de clase sin agregar @classmethod
o @staticmethod
o cualquier definición de @
?
tl; dr: ¿ cuándo debo usarlos, por qué debo usarlos y cómo debo usarlos?
@métodoclase
@classmethod
se puede comparar con __init__
. Podrías pensar que es otro __init__()
. Es la forma en que Python se da cuenta de la sobrecarga del constructor de clases en C++.
class C: def __init__(self, parameters): .... @classmethod def construct_from_func(cls, parameters): .... obj1 = C(parameters) obj2 = C.construct_from_func(parameters)
observe que ambos tienen una referencia para la clase como primer argumento en definición, mientras que __init__
usa self
pero construct_from_func
usa cls
convencionalmente.
@métodoestático
@staticmethod
puede compararse con object method
class C: def __init__(self): .... @staticmethod def static_method(args): .... def normal_method(parameters): .... result = C.static_method(parameters) result = obj.normal_method(parameters)
En resumen, @classmethod convierte un método normal en un método de fábrica.
Vamos a explorarlo con un ejemplo:
class PythonBook: def __init__(self, name, author): self.name = name self.author = author def __repr__(self): return f'Book: {self.name}, Author: {self.author}'
Sin un @classmethod, debe trabajar para crear instancias una por una y están dispersas.
book1 = PythonBook('Learning Python', 'Mark Lutz') In [20]: book1 Out[20]: Book: Learning Python, Author: Mark Lutz book2 = PythonBook('Python Think', 'Allen B Dowey') In [22]: book2 Out[22]: Book: Python Think, Author: Allen B Dowey
Como por ejemplo con @classmethod
class PythonBook: def __init__(self, name, author): self.name = name self.author = author def __repr__(self): return f'Book: {self.name}, Author: {self.author}' @classmethod def book1(cls): return cls('Learning Python', 'Mark Lutz') @classmethod def book2(cls): return cls('Python Think', 'Allen B Dowey')
Pruébalo:
In [31]: PythonBook.book1() Out[31]: Book: Learning Python, Author: Mark Lutz In [32]: PythonBook.book2() Out[32]: Book: Python Think, Author: Allen B Dowey
¿Ver? Las instancias se crean correctamente dentro de una definición de clase y se recopilan juntas.
En conclusión, el decorador @classmethod convierte un método convencional en un método de fábrica. El uso de classmethods hace posible agregar tantos constructores alternativos como sea necesario.
Soy un principiante en este sitio, he leído todas las respuestas anteriores y obtuve la información que quiero. Sin embargo, no tengo derecho a votar. Así que quiero comenzar con StackOverflow con la respuesta tal como la entiendo.
@staticmethod
no necesita self o cls como primer parámetro del método@staticmethod
y @classmethod
podría llamarse por instancia o variable de clase@staticmethod
impacta en algún tipo de 'propiedad inmutable' que la herencia de la subclase no puede sobrescribir su función de clase base que está envuelta por un decorador @staticmethod
.@classmethod
necesita cls (nombre de la clase, puede cambiar el nombre de la variable si lo desea, pero no se recomienda) como primer parámetro de la función@classmethod
siempre se usa a modo de subclase, la herencia de la subclase puede cambiar el efecto de la función de la clase base, es decir, la función de la clase base envuelta @classmethod
podría ser sobrescrita por diferentes subclases.El método de clase puede modificar el estado de la clase, está vinculado a la clase y contiene cls como parámetro.
El método estático no puede modificar el estado de la clase, está vinculado a la clase y no conoce la clase o la instancia
class empDetails: def __init__(self,name,sal): self.name=name self.sal=sal @classmethod def increment(cls,name,none): return cls('yarramsetti',6000 + 500) @staticmethod def salChecking(sal): return sal > 6000 emp1=empDetails('durga prasad',6000) emp2=empDetails.increment('yarramsetti',100) # output is 'durga prasad' print emp1.name # output put is 6000 print emp1.sal # output is 6500,because it change the sal variable print emp2.sal # output is 'yarramsetti' it change the state of name variable print emp2.name # output is True, because ,it change the state of sal variable print empDetails.salChecking(6500)
@classmethod
significa: cuando se llama a este método, pasamos la clase como primer argumento en lugar de la instancia de esa clase (como hacemos normalmente con los métodos). Esto significa que puede usar la clase y sus propiedades dentro de ese método en lugar de una instancia en particular.
@staticmethod
significa: cuando se llama a este método, no le pasamos una instancia de la clase (como hacemos normalmente con los métodos). Esto significa que puede poner una función dentro de una clase pero no puede acceder a la instancia de esa clase (esto es útil cuando su método no usa la instancia).
La respuesta de Rostyslav Dzinko es muy apropiada. Pensé que podría resaltar otra razón por la que debería elegir @classmethod
sobre @staticmethod
cuando está creando un constructor adicional.
En el ejemplo anterior, Rostyslav usó @classmethod
from_string
como una fábrica para crear objetos de Date
a partir de parámetros que de otro modo serían inaceptables. Lo mismo se puede hacer con @staticmethod
como se muestra en el siguiente código:
class Date: def __init__(self, month, day, year): self.month = month self.day = day self.year = year def display(self): return "{0}-{1}-{2}".format(self.month, self.day, self.year) @staticmethod def millenium(month, day): return Date(month, day, 2000) new_year = Date(1, 1, 2013) # Creates a new Date object millenium_new_year = Date.millenium(1, 1) # also creates a Date object. # Proof: new_year.display() # "1-1-2013" millenium_new_year.display() # "1-1-2000" isinstance(new_year, Date) # True isinstance(millenium_new_year, Date) # True
Así, tanto new_year
como millenium_new_year
son instancias de la clase Date
.
Pero, si observa de cerca, el proceso Factory está codificado para crear objetos de Date
sin importar qué. Lo que esto significa es que incluso si la clase de Date
es una subclase, las subclases seguirán creando objetos de Date
sin formato (sin ninguna propiedad de la subclase). Véalo en el siguiente ejemplo:
class DateTime(Date): def display(self): return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year) datetime1 = DateTime(10, 10, 1990) datetime2 = DateTime.millenium(10, 10) isinstance(datetime1, DateTime) # True isinstance(datetime2, DateTime) # False datetime1.display() # returns "10-10-1990 - 00:00:00PM" datetime2.display() # returns "10-10-2000" because it's not a DateTime object but a Date object. Check the implementation of the millenium method on the Date class for more details.
datetime2
no es una instancia de DateTime
? WTF? Bueno, eso se debe al decorador @staticmethod
utilizado.
En la mayoría de los casos, esto no es deseado. Si lo que desea es un método Factory que conozca la clase que lo llamó, entonces @classmethod
es lo que necesita.
Reescribiendo Date.millenium
como (esa es la única parte del código anterior que cambia):
@classmethod def millenium(cls, month, day): return cls(month, day, 2000)
asegura que la class
no esté codificada sino que se aprenda. cls
puede ser cualquier subclase. El object
resultante será correctamente una instancia de cls
.
Probemos eso:
datetime1 = DateTime(10, 10, 1990) datetime2 = DateTime.millenium(10, 10) isinstance(datetime1, DateTime) # True isinstance(datetime2, DateTime) # True datetime1.display() # "10-10-1990 - 00:00:00PM" datetime2.display() # "10-10-2000 - 00:00:00PM"
La razón es, como ya sabe, que se usó @classmethod
en lugar de @staticmethod
La función @staticmethod
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 a través de la herencia.
La función @classmethod
también se puede llamar sin instanciar la clase, pero su definición sigue a la subclase, no a la clase principal, a través de la herencia, puede ser anulada por la subclase. Eso es porque el primer argumento para la función @classmethod
siempre debe ser cls (class)
.
aquí hay un buen enlace a este tema.
una pequeña compilación
@staticmethod Una forma de escribir un método dentro de una clase sin referencia al objeto al que se llama. Por lo tanto, no es necesario pasar argumentos implícitos como self o cls. Está escrito exactamente igual que fuera de la clase, pero no es inútil en python porque si necesita encapsular un método dentro de una clase, ya que este método debe ser parte de esa clase, @staticmethod es útil en eso caso.
@classmethod Es importante cuando desea escribir un método de fábrica y mediante este(s) atributo(s) personalizado(s) se pueden adjuntar en una clase. Estos atributos se pueden anular en la clase heredada.
Una comparación entre estos dos métodos puede ser la siguiente
Uno usaría @classmethod
cuando quisiera cambiar el comportamiento del método en función de qué subclase está llamando al método. recuerde que tenemos una referencia a la clase que llama en un método de clase.
Mientras usa estática, querrá que el comportamiento permanezca sin cambios en las subclases
Ejemplo:
class Hero: @staticmethod def say_hello(): print("Helllo...") @classmethod def say_class_hello(cls): if(cls.__name__=="HeroSon"): print("Hi Kido") elif(cls.__name__=="HeroDaughter"): print("Hi Princess") class HeroSon(Hero): def say_son_hello(self): print("test hello") class HeroDaughter(Hero): def say_daughter_hello(self): print("test hello daughter") testson = HeroSon() testson.say_class_hello() #Output: "Hi Kido" testson.say_hello() #Outputs: "Helllo..." testdaughter = HeroDaughter() testdaughter.say_class_hello() #Outputs: "Hi Princess" testdaughter.say_hello() #Outputs: "Helllo..."
Una forma ligeramente diferente de pensarlo que podría ser útil para alguien... Un método de clase se usa en una superclase para definir cómo debe comportarse ese método cuando lo llaman diferentes clases secundarias. Se usa un método estático cuando queremos devolver lo mismo independientemente de la clase secundaria que estemos llamando.