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中(或者在其他语言中)没有什么了不起的。但是,让替代实现的涌入来吧。。。