我正在扩展现有的PlayerClassFromGameEngine
类,以允许自定义效果仅在一定时间内生效。
示例:
使用原始课程,我会通过说player.move_type = MoveTypes.Freeze
来冻结玩家,然后通过说player.move_type = MoveTypes.Normal
解冻他。
现在我想扩展该类,以便我可以使用函数调用来代替player.freeze(5)
,将玩家冻结五秒钟。
我显然需要两个功能,效果功能和撤销功能,f.e。 freeze()
和unfreeze()
。
这是我目前的课程,工作正常:
class MyPlayer(PlayerClassFromGameEngine):
def __init__(self, index):
super().__init__(index) # Let the original game engine handle this
self.effects = defaultdict(set)
def freeze(self, duration):
self.move_type = MoveType.FREEZE # Move type comes from the super class
thread = threading.Thread(target=self._unfreeze)
thread.args = (duration, thread)
self.effects['freeze'].add(thread)
thread.start()
def _unfreeze(self, duration, thread):
time.sleep(duration)
self.effects['freeze'].remove(thread)
if not self.effects['freeze']: # No more freeze effects
self.move_type = MoveType.NORMAL
如您所见,只有一个效果需要超过10行代码,其中20个代码会很糟糕,因为它们的工作方式完全相同,只是使用不同的键('freeze'
,{{1}等等,而有些人调用函数而不是访问burn
属性。
我基本上没有想法从哪里开始,可能是描述符和装饰者,但有人可以给我一些建议,或者更好的解决方案吗?
修改
这是我在Martijn建议之后得出的结果,但由于我无法访问move_type
类中的玩家
Effect
我认为我需要以某种方式访问from collections import defaultdict
from threading import Thread
from time import sleep
class Effect(object):
def __init__(self, f, undo_f=None):
self.f = f
self.undo_f = undo_f
self._thread = None
def __call__(self, duration):
self._thread = Thread(target=self._execute, args=(duration, ))
self._thread.start()
def _execute(self, duration):
self.f()
sleep(duration)
self.undo_f()
def undo(self, undo_f):
return type(self)(self.f, undo_f)
class Player:
def __init__(self, index):
self.index = index
self._effects = defaultdict(set)
@Effect
def freeze(self):
print('FROZEN')
@freeze.undo
def freeze(self):
print('UNFROZEN')
p = Player(1)
p.freeze(3)
课程内的播放器,因为我无法在Effect
方法中拨打self.f(player)
或self.undo_f(player)
,我也无法访问播放器的Effect._execute
字典。
我想我在任何地方都不需要effects
参数,因为我可以为每个效果生成一个随机数(c中唯一的一个),因为它不会向任何人显示。
答案 0 :(得分:2)
这是一种方法:
from functools import partial
import time
import threading
class aftereffect(object):
def __init__(self, func):
self._func = func
self._aftereffect = lambda instance: None
def aftereffect(self, func):
self._aftereffect = func
return self
def __get__(self, instance, cls):
# this is the descriptor protocol, and
# instance is the actual object
def delayed_effect(timeout):
time.sleep(timeout)
self._aftereffect(instance)
def caller(*args, **kwargs):
timeout = kwargs.pop("_timeout", 1.)
t = threading.Thread(target=partial(delayed_effect, timeout=timeout))
t.start()
return self._func(*args, **kwargs)
return caller.__get__(instance, cls)
class Thing(object):
@aftereffect
def something(self):
print "something", self
@something.aftereffect
def something(self):
print "after_something", self
t = Thing()
t.something()
t.something(_timeout=5)