Как считать полные призывы к методу класса?

Представьте, что для игры имеется класс 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])

Но я хочу знать, возможно ли делать это на уровне класса, как какой-то тип настойчивой переменной в течение всего времени жизни игры. Существует способ это делать?


Замечание:

Пока я хочу поддержать это простым и предотвратить использование Баз данных.


Обновление 1 (ответ @RuslanLópezCarro)

Возможно получать общее количество создавая переменную класса, но это подразумевало бы быть должное увеличивать вручную переменную каждый раз, когда он называется в метод 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

Что мне не кажется выбором, я хочу сделать это автоматически с каждым призывом к методу.

22
задан 09.12.2015, 04:22
6 ответов

Обновление:

Я только что исправил линию кода, которого не было, способствуя тому, чтобы декоратор не призвал в украшенную функцию.

Также я включаю способ облагать налогом призыв более чем одного метода класса.


Лучший способ это осуществлять, по моему мнению, - с одним 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}

Следовательно, рекомендуемые шаги:

  1. Добавлять статическую переменную с начальной стоимостью 0.
  2. Осуществлять метод класса, который, будучи назван только, увеличивал бы статическую переменную предыдущей точки.
  3. Осуществлять метод декоратор, функция которого помещает просто, призвал в метод класса предыдущей точки.
    • Увеличивать счетчик в этой внутренней функции не функционирует.
  4. В конце концов использовать предыдущий декоратор, чтобы украшать функцию, призывы которой мы хотим считать.

------

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()
10
ответ дан 24.11.2019, 15:07
  • 1
    Никол и # 225; s, большое спасибо! Функционируй верно, и #191; qu и # 233; ты думаешь о soluci и # 243; n, что encontr и # 233; я? –  César 09.12.2015, 15:55
  • 2
    Проблема, что я нахожу этот soluci и # 243; n дело в том, что это эквивалент, у, которого должна быть переменная est и # 225; костариканская, и в каждом м и # 233; совсем, в который он est и # 233; s " делая seguimiento" tendr и # 225; s, что увеличивать соответствующую переменную. В любом случае, функционируй, что всегда важно.;) –  Nicolás 09.12.2015, 18:02
  • 3
    В действительности это признак из метода, который единственное увеличение, когда он называет того же самого. Равный я верю в то, что твой soluci и # 243; n - больше Pyth и # 243; nica, check для тебя и из-за твоего большого soluci и # 243; n! –  César 09.12.2015, 18:05
  • 4
    Я верю в то, что этот soluci и # 243; n она не правильна. " contador" от призывов realiza к уровню класс cls.total_renombres += 1 и не инстанция , чем все инстанции класса способствуют счетчику. –  mementum 13.01.2016, 13:55

Объяви твою переменную внутри определения класса

class NaveEspacial:
    total_renombres = 3

Таким образом это будет статическая переменная. Для большей информации ты можешь консультировать этот вопрос.

4
ответ дан 24.11.2019, 15:07
  • 1
    Не я est и # 225; функционируя, и #191; podr и # 237; схвати помещать пример? Всегда эта равная переменная, идея состоит в том, чтобы он увеличился относительно созданных инстанций, и в том, чтобы каждый призыв к renombrar увеличился в 1 –  César 09.12.2015, 03:21
  • 2
    #191; Я должен называть вручную NaveEspacial.total_renombres +=1 каждый раз, когда он называется в м и # 233; все? Идея состоит в том, чтобы он был autom и # 225; костариканский и происходите, когда его называются м и # 233; все –  César 09.12.2015, 03:34
  • 3
    в отличие от других языков как Java, есть доступ инстанции и один из класса, у обоих есть различная стоимость. Ты, должно быть, соглашаешься на глобальную стоимость. –  Ruslan López 09.12.2015, 06:30

Я нашел форму, немного проще, чем предложенная из-за @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 и кажется, что это не является возможным, по крайней мере, не формы, описанной в этом ответе.

3
ответ дан 24.11.2019, 15:07

Лучшая 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).

3
ответ дан 24.11.2019, 15:07

Оставшаяся часть ответов уже дает различные решения, чтобы приносить счет известности начиная с самого класса, но что корабль был должен приносить счет известности всех кораблей пространственного флота, это не хорошая идея. Используя рисунок, ориентируемый на объекты возможно получать больше гибкости и позволь тестировать вещи отдельной формы.

Применяя начало расставания интересов (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
2
ответ дан 24.11.2019, 15:07

Во мне 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
2
ответ дан 24.11.2019, 15:07

Теги

Похожие вопросы