Представьте, что в игре есть класс “Spaceship”, в котором у игроков есть возможность, среди прочего, изменить название корабля после его создания:
class Spaceship: def __init__(self, name, capitan): self.name = name self.capitan = capitan def print_data(self): data = { 'name': self.name, 'capitan': self.capitan } print data def rename(self, name): self.name = name ship = Spaceship('Звезда смерти', 'Цеситар') ship.rename('Тысячелетний сокол') ship.print_data() # Результат {'name': 'Звезда смерти', 'capitan': 'Цеситар'} {'name': 'Тысячелетний сокол', 'capitan': 'Цеситар'}
Если бы я хотел узнать, сколько раз менялось название корабля, я мог бы добавить счетчик:
class Spaceship: def __init__(self, name, capitan): self.name = name self.capitan = capitan self.total_renames = 0 def print_data(self): data = { 'name': self.name, 'capitan': self.capitan, 'total_renames': self.total_renames } print data def rename(self, name): self.name = name self.total_renames += 1 ship = Spaceship('Звезда смерти', 'Цеситар') ship.print_data() ship.rename('Тысячелетний сокол') ship.print_data() # Результат {'name': 'Звезда смерти', 'capitan': 'Цеситар', 'total_renames': 0} {'name': 'Тысячелетний сокол', 'capitan': 'Цеситар', 'total_renames': 1}
Но это поможет мне узнать, сколько раз каждый экземпляр класса, т.е. каждый корабль, был переименован:
ship = Spaceship('Звезда смерти', 'Цеситар')
ship.print_data()
ship.rename('Тысячелетний сокол')
ship.print_data()
ship = Spaceship('USS Enterprise', 'Фьерелла')
ship.print_data()
ship.rename('Центавра')
ship.print_data()
ship.rename('Челенджер')
ship.print_data()
# Результат
{'name': 'Звезда смерти', 'capitan': 'Цеситар', 'total_renames': 0}
{'name': 'Тысячелетний сокол', 'capitan': 'Цеситар', 'total_renames': 1}
{'name': 'Энтерпрайз', 'capitan': 'Фьерелла', 'total_renames': 0}
{'name': 'Центавра', 'capitan': 'Фьерелла', 'total_renames': 1}
{'name': 'Челенджер', 'capitan': 'Фьерелла', 'total_renames': 2}
Code language: PHP (php)
Мне нужно подсчитать общее количество вызовов метода класса, а не общее количество вызовов для каждого экземпляра. Я знаю, что одним из способов может быть накопление всех кораблей и сложение атрибута total_renames каждого из них:
ships = [ship1, ship2, ship3] total = sum([ship.total_renames for ship in ships])
Но я хочу знать, можно ли сделать это на уровне класса, как некую постоянную переменную на время жизни игры. Есть ли способ сделать это?
Примечание: на данный момент я хочу сохранить простоту и не использовать базы данных.
Ответ.
Лучший способ реализовать это, на мой взгляд, это декоратор, который обрабатывает статическую переменную ( добавляю комментарии ко всем методам):
# -*- coding: utf-8 -*- class Spaceship: total_renames = 0 def __init__(self, name, capitan): self.name = name self.capitan = capitan def print_data(self): data = { 'name': self.name, 'capitan': self.capitan, 'total_renames': Spaceship.total_renames } print data def _dec_increases_renames(func): """ Это функция-декоратор, которая отвечает за вызов метода класса _increases_counter_renames. Инкремент не работает, если делать его внутри функции "internal". Последний должен вызывать метод класса. """ def internal(cls, *args, **kwargs): result = func(cls, *args, **kwargs) cls._increases_counter_renames() return result return internal @_dec_increases_renames def rename(self, name): """Метод декорирован с увеличением счетчика.""" self.name = name @classmethod def _increases_counter_renames(cls): """ Это метод класса, который только увеличивает статический счетчик. """ cls.total_renames += 1 ship = Spaceship('Звезда смерти', 'Цесистар') ship.print_data() ship.rename('Тысячелетний сокол') ship.print_data() ship = Spaceship('Энтерпрайз', 'Фьерелла') ship.print_data() ship.rename('Центавра') ship.print_data() ship.rename('Челенджер') ship.print_data()
Вывод этого кода соответствует требованиям:
{'name': 'Звезда смерти', 'capitan': 'Цесистар', 'total_renames': 0}
{'name': 'Тысячелетний сокол', 'capitan': 'Цесистар', 'total_renames': 1}
{'name': 'Энтерпрайз', 'capitan': 'Фьерелла', 'total_renames': 1}
{'name': 'Центавра', 'capitan': 'Фьерелла', 'total_renames': 2}
{'name': 'Челенджер', 'capitan': 'Фьерелла', 'total_renames': 3}
Code language: JavaScript (javascript)
Поэтому рекомендуются следующие шаги:
- Добавьте статическую переменную с начальным значением 0.
- Реализуйте метод класса, который при вызове только увеличивает статическую переменную с предыдущего значения.
- Реализуйте метод-декоратор, внутренняя функция которого просто вызывает метод класса из предыдущего пункта.
- Увеличение счетчика в этой внутренней функции не работает.
- Используйте вышеупомянутый декоратор для декорирования функции, вызовы которой мы хотим подсчитать.