为复杂和重复的函数调用创建类似 属性 的解决方案
Creating a property-like solution for complicated and repetitive function calls
我正在扩展现有的 PlayerClassFromGameEngine
class 以允许自定义效果仅在特定持续时间内生效。
示例:
使用原始的 class,我会说 player.move_type = MoveTypes.Freeze
冻结玩家,然后说 player.move_type = MoveTypes.Normal
解冻他。
现在我想扩展 class,这样我就可以改用函数调用:player.freeze(5)
,将播放器冻结五秒钟。
我显然需要两个函数,效果函数和撤消函数,f.e。 freeze()
和 unfreeze()
。
这是我当前的 class,效果很好:
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'
, burn
, 等等),有些调用函数而不是访问 move_type
属性.
我基本上不知道从哪里开始,也许是描述符和装饰器,但有人能给我一些建议,或者更好的工作解决方案吗?
编辑:
这是我根据 Martijn 的建议想到的,但它不起作用,因为我无法访问 Effect
class
中的播放器
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
class 内部的播放器,因为我无法在 self.f(player)
或 self.undo_f(player)
中调用=26=] 方法,我也无法访问播放器的 effects
字典。
我想我在任何地方都不需要 key
参数,因为我可以为每个效果生成一个随机数(唯一的一个),因为它不会显示给任何人。
这是一个可行的方法:
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)
我正在扩展现有的 PlayerClassFromGameEngine
class 以允许自定义效果仅在特定持续时间内生效。
示例:
使用原始的 class,我会说 player.move_type = MoveTypes.Freeze
冻结玩家,然后说 player.move_type = MoveTypes.Normal
解冻他。
现在我想扩展 class,这样我就可以改用函数调用:player.freeze(5)
,将播放器冻结五秒钟。
我显然需要两个函数,效果函数和撤消函数,f.e。 freeze()
和 unfreeze()
。
这是我当前的 class,效果很好:
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'
, burn
, 等等),有些调用函数而不是访问 move_type
属性.
我基本上不知道从哪里开始,也许是描述符和装饰器,但有人能给我一些建议,或者更好的工作解决方案吗?
编辑:
这是我根据 Martijn 的建议想到的,但它不起作用,因为我无法访问 Effect
class
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
class 内部的播放器,因为我无法在 self.f(player)
或 self.undo_f(player)
中调用=26=] 方法,我也无法访问播放器的 effects
字典。
我想我在任何地方都不需要 key
参数,因为我可以为每个效果生成一个随机数(唯一的一个),因为它不会显示给任何人。
这是一个可行的方法:
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)