为复杂和重复的函数调用创建类似属性的解决方案

时间:2015-03-10 21:20:28

标签: python python-3.x properties descriptor

我正在扩展现有的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中唯一的一个),因为它不会向任何人显示。

1 个答案:

答案 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)