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

Python 为复杂和重复的函数调用创建类似属性的解决方案,python,python-3.x,properties,descriptor,Python,Python 3.x,Properties,Descriptor,我正在扩展一个现有的PlayerClassFromGameEngine类,以允许自定义效果仅在特定时间内生效 示例: 使用原始类,我会通过说player.move\u type=MoveTypes.freeze冻结一个玩家,然后通过说player.move\u type=MoveTypes.Normal解冻他。 现在我想扩展这个类,这样我就可以使用函数调用了:player.freeze(5),将播放器冻结五秒钟 我显然需要两个函数,效果函数和撤销函数,例如freeze()和unfreeze()。

我正在扩展一个现有的
PlayerClassFromGameEngine
类,以允许自定义效果仅在特定时间内生效

示例: 使用原始类,我会通过说
player.move\u type=MoveTypes.freeze
冻结一个玩家,然后通过说
player.move\u type=MoveTypes.Normal
解冻他。 现在我想扩展这个类,这样我就可以使用函数调用了:
player.freeze(5)
,将播放器冻结五秒钟

我显然需要两个函数,效果函数和撤销函数,例如
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'
burn
,等等),有些调用函数而不是访问
move_type
属性

我基本上不知道从哪里开始,也许是描述符和装饰师,但有人能给我一些建议,或者更好的解决方案吗


编辑: 下面是我在Martijn的建议后想到的,但它不起作用,因为我无法访问
效果
类中的播放器

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\u f(player)
效果中的
方法,也不能访问播放器的
effects
字典。
我想我在任何地方都不需要
参数,因为我可以为每个效果生成一个随机数(一个唯一的ofc),因为它不会显示给任何人。

这将是一种方法:

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)

你的装饰师很好;你为什么认为这是不可能的?您的
@effect()
装饰程序需要做的就是注册取消冻结。您可以使用
@freeze.undo
作为另一个
def freeze()
方法上的装饰器,就像
@property
@propertyname.setter
一样;看看它是如何工作的。@MartijnPieters没有办法将这两个方法连接在一起,以便从
effects
字典中删除正确的
线程,至少我想不出一个方法。我从来没有使用过描述符,我对装饰师的了解也很少,也许我就是想不出一个解决方案。。这就是为什么我首先要寻求帮助:>我现在没有时间写下完整的答案;但是我建议你仔细阅读
属性
装饰器是如何工作的(请参阅我之前评论中的链接),然后从那里开始。@MartijnPieters好的,我想到了一些东西,这不是一个有效的解决方案,但我认为我走的方向是正确的。我编辑了这个问题,以便你能看到我的答案,但如果你有空的话,我很想得到一些帮助。:)我现在得去睡觉了,但我每天都会在这里检查一下。你快到了,但是错过了一个关键点——阅读描述符协议。这就是@MartijnPieters在谈论房产装饰师时的意思。这样,您的Effec类实现了一个get方法,该方法将为该特定调用传递当前播放器实例。另一方面:我认为你应该重新考虑使用线程来实现你的方法,而应该使用gameengine/system/libraries的事件/计时器系统。否则,线程会很多,问题也会很多。这不允许我为每个调用使用自定义的
持续时间。我想做一些类似于
t.something(5)
的事情,而不是静态睡眠值。如前所述,它不允许我为每个调用使用自定义的持续时间。我并不总是想冻结玩家5秒,我有时想冻结他3秒,有时30秒。误解了,现在是每次通话,仍然不确定,似乎有点不对劲。。。在一个函数中定义两个函数只是为了访问另一个函数。我将等待更多的答案,但这总比什么都没有好:)定义本地函数在python中(或者在其他语言中)没有什么了不起的。但是,让替代实现的涌入来吧。。。