Python: Передача статического метода в конструктор другого класса
У меня есть три класса:
- Класс, представляющий пункт меню:
class MenuItem:
def __init__(self, title, function):
self.title = title
self.function = function
def execute(self, *args, **kwargs):
return self.function(*args, **kwargs)
- Класс, представляющий меню:
class Menu:
def __init__(self, title, items):
self.items = items
self.title = title
- Статический класс, представляющий интерфейс:
class GameUI:
mainmenu = Menu("", [
MenuItem("1. Start the game", None),
MenuItem("2. Settings", None),
MenuItem("3. Exit", GameUI._exit)
])
@classmethod
def _exit(cls):
pass
Когда я пытаюсь проинициализировать переменную mainmenu в классе GameUI, то получаю ошибку о том, что имя GameUI не определено, если же передавать функцию _exit без указания класса, то при вызове функции приходится передавать и класс: GameUI.gamemenu.items[2].execute(GameUI)
Ответы (3 шт):
Автор решения: CrazyElf
→ Ссылка
Питон интерпретирует последовательно в каких-то случаях. Поэтому тут сначала нужно определить функцию, а потом на неё ссылаться. Так уже не ругается:
class GameUI:
@classmethod
def _exit(cls):
pass
mainmenu = Menu("", [
MenuItem("1. Start the game", None),
MenuItem("2. Settings", None),
MenuItem("3. Exit", GameUI._exit)
])
Автор решения: Tr1nks
→ Ссылка
Простой вариант - вынести инициализацию за пределы класса.
Более сложный вариант - метакласс.
# вариант 1
class GameUI1:
@classmethod
def _exit(cls):
print('GameUI1 exit')
GameUI1.mainmenu = Menu("", [
MenuItem("1. Start the game", None),
MenuItem("2. Settings", None),
MenuItem("3. Exit", GameUI1._exit)
])
#вариант 2
class GameUIMeta(type):
def __new__(cls, *args, **kwargs):
menu_cls = super(GameUIMeta, cls).__new__(cls,*args,**kwargs)
menu_cls.mainmenu = Menu("", [
MenuItem("1. Start the game", None),
MenuItem("2. Settings", None),
MenuItem("3. Exit", menu_cls._exit)
])
return menu_cls
class GameUI2(metaclass=GameUIMeta):
@classmethod
def _exit(cls):
print('GameUI2 exit')
gu1 = GameUI1()
gu2 = GameUI2()
gu1.mainmenu.items[2].execute()
gu2.mainmenu.items[2].execute()
Автор решения: Sour Sourse
→ Ссылка
Только python 3.x.
Можно несколько иначе реализовать метод __get__ для объекта свойства
class classproperty: # создали свойство (тн дескриптор не данных)
def __init__(self, fget):
self.fget = fget
def __get__(self, instance, objtype):
return self.fget(objtype) # обычно передаётся instance вместо objtype
class GameUI:
mainmenu: Menu
@classmethod
def _exit(cls):
pass
@classproperty
def mainmenu(cls): # первое обращение к свойству инициализирует объект
print('call parent class')
setattr(cls, 'mainmenu', Menu("", [MenuItem("1. Start the game", None),
MenuItem("2. Settings", None),
MenuItem("3. Exit", cls._exit)]))
return cls.mainmenu # все последующие обращения будут возвращать атрибут самого класса