Imagine that for a game you have a class NaveEspacial
in which players have the ability, among other things, to change the name of the ship after having created it:
class NaveEspacial:
def __init__(self, nombre, capitan):
self.nombre = nombre
self.capitan = capitan
def imprimir_datos(self):
datos = {
'nombre': self.nombre,
'capitan': self.capitan
}
print datos
def renombrar(self, nombre):
self.nombre = nombre
nave = NaveEspacial('Death Star', 'Cesitar')
nave.renombrar('Millennium Falcon')
nave.imprimir_datos()
# Resultado
{'nombre': 'Death Star', 'capitan': 'Cesitar'}
{'nombre': 'Millennium Falcon', 'capitan': 'Cesitar'}
If you wanted to know how many times the ship's name has been changed, you could add a counter:
class NaveEspacial:
def __init__(self, nombre, capitan):
self.nombre = nombre
self.capitan = capitan
self.total_renombres = 0
def imprimir_datos(self):
datos = {
'nombre': self.nombre,
'capitan': self.capitan,
'total_renombres': self.total_renombres
}
print datos
def renombrar(self, nombre):
self.nombre = nombre
self.total_renombres += 1
nave = NaveEspacial('Death Star', 'Cesitar')
nave.imprimir_datos()
nave.renombrar('Millennium Falcon')
nave.imprimir_datos()
# Resultado
{'nombre': 'Death Star', 'capitan': 'Cesitar', 'total_renombres': 0}
{'nombre': 'Millennium Falcon', 'capitan': 'Cesitar', 'total_renombres': 1}
But the above would only help me know how many times each instance of the class, that is, each ship, has been renamed:
nave = NaveEspacial('Death Star', 'Cesitar')
nave.imprimir_datos()
nave.renombrar('Millennium Falcon')
nave.imprimir_datos()
nave = NaveEspacial('USS Enterprise', 'Fiorella')
nave.imprimir_datos()
nave.renombrar('USS Centaur')
nave.imprimir_datos()
nave.renombrar('USS Challenger')
nave.imprimir_datos()
# Resultado
{'nombre': 'Death Star', 'capitan': 'Cesitar', 'total_renombres': 0}
{'nombre': 'Millennium Falcon', 'capitan': 'Cesitar', 'total_renombres': 1}
{'nombre': 'USS Enterprise', 'capitan': 'Fiorella', 'total_renombres': 0}
{'nombre': 'USS Centaur', 'capitan': 'Fiorella', 'total_renombres': 1}
{'nombre': 'USS Challenger', 'capitan': 'Fiorella', 'total_renombres': 2}
What I need is to count the total calls to the class method and not the total for each instance . I know that one way could be to accumulate all the ships and add the attribute total_renombres
of each one:
naves = [nave1, nave2, nave3]
total = sum([nave.total_renombres for nave in naves])
But I want to know if it's possible to do this at the class level, as some kind of persistent variable for the lifetime of the game. Is there a way to do it?
Note:
For now I want to keep it simple and avoid using Databases.
Update 1 (answer from @RuslanLópezCarro)
It is possible to get the total by creating a variable of the class, but that would mean having to manually increment the variable each time the method is called renombrar()
:
nave1 = NaveEspacial('Death Star', 'Cesitar')
nave1.renombrar('Millennium Falcon')
NaveEspacial.total_llamadas += 1 # Incrementar
nave2 = NaveEspacial('USS Enterprise', 'Fiorella')
nave2.renombrar('USS Centaur')
NaveEspacial.total_llamadas += 1 # Incrementar
nave2.renombrar('USS Challenger')
NaveEspacial.total_llamadas += 1 # Incrementar
nave3 = NaveEspacial('ABC', 'Tany')
nave3.renombrar('XYZ')
NaveEspacial.total_llamadas += 1 # Incrementar
print NaveEspacial.total_llamadas
# Resultado 4
Which doesn't seem like an option to me, I want to do it automatically with each method call.
Update :
I just fixed a missing line of code, making the decorator not call the decorated function.
I also include the way to count the invocation of more than one method of the class.
The best way to implement this, in my opinion, is with a
decorador
static variable handler (I add comments to all methods written by me):The output of this code is as expected:
Therefore, the recommended steps are:
0
.------
Bonuses:
To implement a count of the invocations of more than one method, a small change must be made to the decorator function, essentially so that it discerns which function it is decorating, and therefore knows which counter to order its increment:
Declare your variable inside the class definition
That way it will be a static variable. For more information you can refer to this question .
I found a way, a bit simpler than the one provided by @Nicolás but a bit more "obscure" at the same time.
According to the documentation on methods , it is possible to assign attributes to them arbitrarily by making use of the special read-only attribute called
im_func
provided to instance methods of classes.This is not possible:
This if possible:
In this way it is possible to define the class in this way:
And in this way I was able to have a general counter and one for each instance.
Note:
I just tried it in Python 3.x and it seems that this is not possible, at least not in the way described in this answer.
The best solution is to use descriptors as class attributes.
The code could be something like this:
For use, an instance is created and assigned to an attribute of the class
NaveEspacial
:This code works correctly almost every time. However, there is still the possibility of "mashing" the class attribute with an arbitrary value (eg:
NaveEspacial.total_renombres = 0
). If you need to hide it altogether, use "name mangling" , that is, rename the attribute with double underscores (eg:__total_renombres
).The rest of the answers already give different solutions to keep track of the renown from the class itself, but that a ship has to keep track of the renowns of all the ships in the space fleet is not a good idea. Using object-oriented design gives you more flexibility and allows you to test things separately.
Applying the separation of concerns principle, a new class can be created that is responsible for keeping track of ships and renowns.
So that each ship finds out which command it has to report to, it has to be indicated, this is called dependency injection
When renaming the ship we can consult the command what ships there are
Si no queremos andar especificando a cada nave a que comandancia ha de rendir cuentas podemos crear una factoría que se encargue de inyectar esa dependencia directamente y así no tener que especificarlo para todas las naves:
En mi opinión una de las mejores soluciones pasa por combinar conceptos expuestos en las otras respuestas en el caso de querer aplicarla a métodos concretos de una clase.
Utilizando un
descriptor
para decorar el método (que durante la decoración todavía es función) para controlar el accesso (get/set) al atributoUtilizando una clase auxiliar para envolver a la función convertida en método y controlar las llamadas al método
Utilizando el acceso al descriptor a nivel de clase, para poder acceder a sus métodos (como p.ej el que devuelve el número de renombrados)
De esta manera:
Podemos llevar la cuenta por clase
Evitamos que el atributo sea sobre-escribible a nivel de instancia (a nivel de clase lo es)
Código:
Resultado: