Представьте, что для игры имеется класс NaveEspacial
в которой у игроков есть способность, между другими вещами, меняния имени корабля, после создав ее раньше:
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'}
Если бы он захотел знать все разы, изменилось имя корабля, он мог бы добавлять счетчик:
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}
Но одинокое предыдущее помогло бы мне знать все разы была названа каждая инстанция класса, а именно, каждый корабль:
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}
То, в чем я нуждаюсь, состоит в том, чтобы считать полные призывы к методу класса и не общего количества из-за каждой инстанции. Что форма могла бы состоять в том, чтобы накапливать все корабли и складывать признак total_renombres
каждой:
naves = [nave1, nave2, nave3]
total = sum([nave.total_renombres for nave in naves])
Но я хочу знать, возможно ли делать это на уровне класса, как какой-то тип настойчивой переменной в течение всего времени жизни игры. Существует способ это делать?
Замечание:
Пока я хочу поддержать это простым и предотвратить использование Баз данных.
Возможно получать общее количество создавая переменную класса, но это подразумевало бы быть должное увеличивать вручную переменную каждый раз, когда он называется в метод 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
Что мне не кажется выбором, я хочу сделать это автоматически с каждым призывом к методу.
Обновление:
Я только что исправил линию кода, которого не было, способствуя тому, чтобы декоратор не призвал в украшенную функцию.
Также я включаю способ облагать налогом призыв более чем одного метода класса.
Лучший способ это осуществлять, по моему мнению, - с одним decorador
что манипулировал статической переменной (я добавляю комментарии ко всем методам, написанным мной):
# -*- coding: utf-8 -*-
class NaveEspacial:
total_renombres = 0
def __init__(self, nombre, capitan):
self.nombre = nombre
self.capitan = capitan
def imprimir_datos(self):
datos = {
'nombre': self.nombre,
'capitan': self.capitan,
'total_renombres': NaveEspacial.total_renombres
}
print datos
def _dec_incrementa_renombres(func):
"""Esta es la función decoradora, que se encarga
de invocar al método de clase _incrementa_contador_renombres.
El incremento no funciona haciéndolo dentro de la función
"interno". Ésta última debe invocar al método de clase.
"""
def interno(cls, *args, **kwargs):
resultado = func(cls, *args, **kwargs)
cls._incrementa_contador_renombres()
return resultado
return interno
@_dec_incrementa_renombres
def renombrar(self, nombre):
"""Método decorado con el incremento del contador."""
self.nombre = nombre
@classmethod
def _incrementa_contador_renombres(cls):
"""Es un método de clase que solamente incrementa
el contador estático.
"""
cls.total_renombres += 1
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()
Вывод этого кода - ожидаемый:
{'nombre': 'Death Star', 'capitan': 'Cesitar', 'total_renombres': 0}
{'nombre': 'Millennium Falcon', 'capitan': 'Cesitar', 'total_renombres': 1}
{'nombre': 'USS Enterprise', 'capitan': 'Fiorella', 'total_renombres': 1}
{'nombre': 'USS Centaur', 'capitan': 'Fiorella', 'total_renombres': 2}
{'nombre': 'USS Challenger', 'capitan': 'Fiorella', 'total_renombres': 3}
Следовательно, рекомендуемые шаги:
0
.Bonus:
Чтобы осуществлять балансирование призывов более чем одного метода, нужно делать маленькое изменение в функцию декоратор, основательно для того, чтобы он различил, какую функцию он украшает, и следовательно будьте можно какого счетчика упорядочивать Ваше увеличение:
# -*- coding: utf-8 -*-
class NaveEspacial:
total_renombres = 0
total_impresiones = 0
def __init__(self, nombre, capitan):
self.nombre = nombre
self.capitan = capitan
def _dec_incrementa(func):
"""Esta es la función decoradora que se encarga
de incrementar el contador que corresponda según la función
que fue invocada.
"""
def interno(cls, *args, **kwargs):
resultado = func(cls, *args, **kwargs)
if func.__name__ == 'renombrar':
cls._incrementa_contador_renombres()
elif func.__name__ == 'imprimir_datos':
cls._incrementa_contador_impresiones()
return resultado
return interno
@_dec_incrementa
def imprimir_datos(self):
datos = {
'nombre': self.nombre,
'capitan': self.capitan,
'total_renombres': NaveEspacial.total_renombres,
'total_impresiones': NaveEspacial.total_impresiones
}
print datos
@_dec_incrementa
def renombrar(self, nombre):
"""Método decorado con el incremento del contador."""
self.nombre = nombre
@classmethod
def _incrementa_contador_renombres(cls):
"""Es un método de clase que solamente incrementa
el contador estático.
"""
cls.total_renombres += 1
@classmethod
def _incrementa_contador_impresiones(cls):
"""Es un método de clase que solamente incrementa
el contador estático.
"""
cls.total_impresiones += 1
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()
Объяви твою переменную внутри определения класса
class NaveEspacial:
total_renombres = 3
Таким образом это будет статическая переменная. Для большей информации ты можешь консультировать этот вопрос.
renombrar
увеличился в 1
– César
09.12.2015, 03:21
NaveEspacial.total_renombres +=1
каждый раз, когда он называется в м и # 233; все? Идея состоит в том, чтобы он был autom и # 225; костариканский и происходите, когда его называются м и # 233; все
– César
09.12.2015, 03:34
Я нашел форму, немного проще, чем предложенная из-за @Nicolás, но немного "темнее" одновременно.
Согласно документации по методам, возможно распределять им признаки способа arbtiraria используя специальный признак одинокого чтение, названный im_func
предоставляемый методам инстанции классов.
Это не является возможным:
>>> class Clase:
... def metodo(self):
... pass
...
>>> Clase.metodo.total = 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'instancemethod' object has no attribute 'total'
Это, если возможно:
>>> class Clase:
... def metodo(self):
... pass
...
>>> Clase.metodo.im_func.total = 0
>>> Clase.metodo.total
0
>>> # El total se acumula entre las instancias
>>> c1 = Clase()
>>> c1.metodo.im_func.total += 5
>>> c2 = Clase()
>>> c2.metodo.im_func.total += 10
>>> c3 = Clase()
>>> c3.metodo.im_func.total += 15
>>> Clase.metodo.total
30
Таким образом, возможно определять класс таким образом:
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
@classmethod
def imprimir_total_llamadas(cls):
total = 0
if hasattr(cls.renombrar.im_func, 'total_llamadas'):
total = cls.renombrar.total_llamadas
print total
def renombrar(self, nombre):
# En la primera llamada verificamos que exista el atributo
if not hasattr(self.renombrar.im_func, 'total_llamadas'):
self.renombrar.im_func.total_llamadas = 0
self.nombre = nombre
self.total_renombres += 1
self.renombrar.im_func.total_llamadas += 1
NaveEspacial.imprimir_total_llamadas()
nave1 = NaveEspacial('Death Star', 'Cesitar')
nave1.renombrar('Millennium Falcon')
nave1.renombrar('Elysium')
nave1.imprimir_datos()
nave2 = NaveEspacial('USS Enterprise', 'Fiorella')
nave2.renombrar('USS Centaur')
nave2.renombrar('USS Challenger')
nave2.imprimir_datos()
nave3 = NaveEspacial('ABC', 'Tany')
nave3.renombrar('XYZ')
nave3.imprimir_datos()
NaveEspacial.imprimir_total_llamadas()
# Resultado
0
{'nombre': 'Elysium', 'capitan': 'Cesitar', 'total_renombres': 2}
{'nombre': 'USS Challenger', 'capitan': 'Fiorella', 'total_renombres': 2}
{'nombre': 'XYZ', 'capitan': 'Tany', 'total_renombres': 1}
5
И, таким образом, мне смогло удаваться иметь общий счетчик и один каждой инстанцией.
Замечание:
Я только что протестировал это в Пайтоне 3.x и кажется, что это не является возможным, по крайней мере, не формы, описанной в этом ответе.
Лучшая soluciГіn состоит в том, чтобы использовать [таких 115] описывающих как признаки класса.
cГіdigo podrГ, - чтобы быть чем-то asГ-:
# creación del descriptor
class NumRenombres(object):
def __init__(self, default = 0):
self.num = default
def __get__(self, instance, owner):
return self.num
def __set__(self, instance, value):
self.num = value
def __delete__(self, instance):
pass
Для Вашего использования, создает инстанцию и распределяет ее ему в признак класса NaveEspacial
:
class NaveEspacial(object):
total_renombres = NumRenombres()
def renombrar(self):
self.total_renombres += 1
Восток cГіdigo функционирует правильно в почти всех случаях. AГєn asГ, - aГєn следует возможность "разбить" признак класса с произвольной стоимостью (eg: NaveEspacial.total_renombres = 0
). Если ты нуждаешься в том, чтобы скрыть это совсем, используй "ямс mangling" , или же, назови признак с двойной порцией, подчеркнутой (eg: __total_renombres
).
Оставшаяся часть ответов уже дает различные решения, чтобы приносить счет известности начиная с самого класса, но что корабль был должен приносить счет известности всех кораблей пространственного флота, это не хорошая идея. Используя рисунок, ориентируемый на объекты возможно получать больше гибкости и позволь тестировать вещи отдельной формы.
Применяя начало расставания интересов (separation of concerns) могут создают новый класс, который был бы ответственным лицом того, чтобы приносить счет кораблей и известности.
class ComandanciaEspacial:
def __init__(self):
self.naves_renombradas = 0
self.naves_totales = 0
def matricular_nave(self):
self.naves_totales += 1
def rematricular_nave(self):
self.naves_renombradas += 1
def __repr__(self):
t = "La flota estelar tiene {} naves, que en total se han renombrado {} veces"
return t.format(self.naves_totales, self.naves_renombradas
>>> comandancia_rebelde = ComandanciaEspacial()
>>> comandancia_rebelde
La flota estelar tiene 0 naves, que en total se han renombrado 0 veces
Для того, чтобы каждый корабль узнал, которому комендатура должна давать отчеты, он должен показывать его ему, к этому называют ее вставкой dependecias
class NaveEspacial:
def __init__(self, nombre, capitan, comandancia):
self.nombre = nombre
self.capitan = capitan
self._comandancia = comandancia
self._comandancia.matricular_nave()
def renombrar(self, nuevo_nombre):
self._comandancia.rematricular_nave()
self.nombre = nuevo_nombre
def _repr__(self):
return "Nave: '{}', capitan: '{}'".format(self.nombre, self.capitan
>>> halcon = NaveEspacial('YT-1300', 'Han Solo', comandancia_rebelde)
>>> halcon
Nave: 'YT-1300', capitan: 'Han Solo'
>>> comandancia_rebelde
La flota estelar tiene 1 naves, que en total se han renombrado 0 veces
Назвав корабль мы можем консультировать в комендатуру, что корабли есть
>>> halcon.renombrar('Halcon milenario')
>>> halcon
Nave: 'Halcon milenario', capitan: 'Han Solo'
>>> comandancia_rebelde
La flota estelar tiene 1 naves, que en total se han renombrado 1 veces
Если мы не хотим определить в каждый корабль, в который комендатура должна давать отчеты, мы можем создавать факторию, которая бралась бы за то, чтобы делать инъекцию этой зависимости прямо и так не быть должным определять это для всех кораблей:
class AstilleroEspacial:
def __init__(self, comandancia):
self._comandancia = comandancia
def nueva_nave(self, nombre_nave, capitan):
nave = NaveEspacial(nombre_nave, capitan, self._comandancia)
return nave
>>> hoth = AstilleroEspacial(comandancia_rebelde)
>>> wing = hoth.nueva_nave('X-Wing', 'Luke Skywalker')
>>> wing
Nave: 'X-Wing', capitan: 'Luke Skywalker'
>>> comandancia_rebelde
La flota estelar tiene 2 naves, que en total se han renombrado 1 veces
Во мне opiniГіn одно из лучших решений происходит из-за того, что сочетает концепции, выставленные в других ответах в случае хотения применить ее в mГ©todos конкретные класса.
Используя один descriptor
, чтобы украшать mГ©todo (что в течение нее decoraciГіn todavГ - в es funciГіn), чтобы контролировать accesso (get/set) в признак
Используя вспомогательный класс, чтобы завертывать в нее funciГіn конвертируемая в mГ©todo и контролировать вызовы в mГ©todo
Используя доступ к описывающему к уровню класса, чтобы мочь соглашаться в Ваш mГ©todos (как p.ej тот, кто возвращает nГєmero названные)
Таким образом:
мы Можем приносить счет из-за класса
Избегаем того, чтобы признак был на - escribible на уровне инстанции (на уровне класса он это)
CГіdigo: Последованные
import collections
class MethodCounter(object):
class CounterHelper(object):
def __init__(self, parent, method):
self.parent = parent
self.method = method
def __call__(self, *args, **kwargs):
self.parent.increase(self.method)
return self.method(*args, **kwargs)
def __init__(self, func):
self.counter = collections.defaultdict(int)
self.func = func
self.cache = dict()
def __set__(self, obj, value):
raise AttributeError('No se puede modificar')
def __get__(self, obj, cls=None):
if obj is None:
return self
try:
return self.cache[obj]
except KeyError:
method = self.func.__get__(obj, cls)
ch = self.__class__.CounterHelper(self, method)
self.cache[obj] = ch
return ch
def increase(self, method):
try:
obj = method.im_self # Py2
except:
obj = method.__self__ # Py3
self.counter[obj] += 1
def count(self, obj):
return self.counter[obj]
class Nave(object):
def __init__(self, nombre='Nombre'):
self.nombre = nombre
@MethodCounter
def renombrar(self, nombre):
print('renombrando')
self.nombre = nombre
nave = Nave('Halcon')
nave.renombrar('Milenario')
print('Renombrados nave:', Nave.renombrar.count(nave))
nave2 = Nave('Estrella')
nave2.renombrar('De la')
nave2.renombrar('Muerte')
print('Renombrados nave:', Nave.renombrar.count(nave))
print('Renombrados nave2:', Nave.renombrar.count(nave2))
try:
nave.renombrar = 3
except AttributeError:
print('No se puede modificar el método en una instancia')
Nave.renombrar = 5
print('Siempre se puede modificar el atributo a nivel de clase')
:
renombrando
Renombrados nave: 1
renombrando
renombrando
Renombrados nave: 1
Renombrados nave2: 2
No se puede modificar el método en una instancia
Siempre se puede modificar el atributo a nivel de clase
cls.total_renombres += 1
и не инстанция , чем все инстанции класса способствуют счетчику. – mementum 13.01.2016, 13:55