我有一个带有几个方法的类,其中一些只在对象处于特定状态时才有效。我希望这些方法在它们处于合适的状态时不被绑定到对象上,这样我得到的结果如下:
>>> wiz=Wizard()
>>> dir(wiz)
['__doc__', '__module__', 'addmana']
>>> wiz.addmana()
>>> dir(wiz)
['__doc__', '__module__', 'addmana', 'domagic']
>>> wiz.domagic()
>>> dir(wiz)
['__doc__', '__module__', 'addmana']
>>> wiz.domagic()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Wizard instance has no attribute 'domagic'
我可以看到如何添加方法(types.MethodType(方法,对象)),但我看不到任何方法只删除单个对象的方法:
>>> wiz.domagic
<bound method Wizard.domagic of <__main__.Wizard instance at 0x7f0390d06950>>
>>> del wiz.domagic
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Wizard instance has no attribute 'domagic'
覆盖__dir__(并且在调用时获取InvalidState或NotEnoughMana异常而不是在引用时获得AttributeError)可能没问题,但我无法看到如何准确地模仿dir()的内置行为。 (理想情况下,我更喜欢在Python 2.5中运行的方式)
想法?
答案 0 :(得分:10)
您无法从该类的实例中删除类方法,因为该实例不拥有该方法。协议是:如果o
是类Foo
的实例,并且我调用o.bar()
,则会检查第一个o
以查看它是否具有名为{{1}的方法}。如果没有,则检查bar
。除非它们覆盖它的类,否则这些方法不会绑定到实例。
我也没有看到你在这里的道路上有什么好处。
答案 1 :(得分:9)
这不是直接回答您的问题,但我认为解决此问题的更好方法是不添加/删除domagic
上的成员方法wiz
对象:
我要做的是在domagic
方法中,添加一个条件来检查wiz
对象的相关状态,然后只执行domagic
方法的其余部分对于有效状态,否则输出您选择的错误消息。
def domagic():
if (state != desired_state):
print "You cannot do any magic now!"
return
print "Doing some magic"
[some more commands]
答案 2 :(得分:6)
如果某个对象处于调用该对象上的给定方法没有意义的状态,那么将它从对象的界面中消失似乎是处理问题的错误方法。更明确的错误处理使您能够更准确地向调用者解释问题,抛出NotEnoughMana而不是使用方法未找到错误来呈现它们。
如果你担心有一堆看起来像这样的函数前缀:
if self.StateNotValid():
raise NotEnoughMana()
...然后你可以在每个函数上使用装饰器。它更短,并为您提供了一种简单的方法来满足所有需要特殊状态的功能。
答案 3 :(得分:3)
默认情况下,dir()的工作方式如下:
dir(obj) == sorted(obj.__dict__.keys() + dir(obj.__class__))
(好吧,无论如何都要删除重复)
所以方法是:
class Wizard(object):
def __init__(self):
self.mana = 0
def __dir__(self):
natdir = set(self.__dict__.keys() + dir(self.__class__))
if self.mana <= 0:
natdir.remove("domagic")
return list(natdir)
def addmana(self):
self.mana += 1
def domagic(self):
if self.mana <= 0:
raise NotEnoughMana()
print "Abracadabra!"
self.mana -= 1
Py2.6中的行为是:
>>> wiz = Wizard()
>>> [x for x in dir(wiz) if not x.startswith("_")]
['addmana', 'mana']
>>> wiz.addmana()
>>> [x for x in dir(wiz) if not x.startswith("_")]
['addmana', 'domagic', 'mana']
>>> wiz.domagic()
Abracadabra!
>>> [x for x in dir(wiz) if not x.startswith("_")]
['addmana', 'mana']
>>> wiz.domagic()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 13, in domagic
__main__.NotEnoughMana
答案 4 :(得分:2)
你可以使用这样的黑客:
>>> class A(object):
... def test(self):
... print 'test'
...
>>> a = A()
>>> def noattr(name):
... raise AttributeError('no attribute %s' % name)
...
>>> a.test = lambda *a, **k: noattr('test')
>>> a.test()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/Users/piranha/<ipython console> in <module>()
/Users/piranha/<ipython console> in <lambda>(*a, **k)
/Users/piranha/<ipython console> in noattr(name)
AttributeError: no attribute test
当然,这会给你错误的追溯,但异常是一样的。 ; - )
另一种(恕我直言 - 更好)方法是覆盖__getattr__
,然后将调度逻辑放在那里,然后就可以像这样使用它:
>>> class A(object):
... def __getattr__(self, name):
... def inner(*args, **kwargs):
... print args, kwargs
... return inner
...
>>> a = A()
>>> a.test
<function inner at 0x1006c36e0>
>>> a.test('q')
('q',) {}
答案 5 :(得分:1)
似乎有一些非常好的想法来解决如何从python对象隐藏/删除方法的明确问题,所以我想解决一个更高级别的问题。
具体来说,原始问题的问题陈述:
我有一个有几种方法的课, 其中一些只有在有效时才有效 对象处于特定状态。
这种情况是通过State Design Pattern解决问题的典型例子:
此模式用于计算机 编程来表示状态 一个东西。这是一个干净的方式 对象部分改变其类型 运行时。
如果向导具有以下属性,我会考虑使用此模式:
答案 6 :(得分:1)
作为一种额外的可能性(稍微扭转一下这个问题),如果在某些实例上只有某些方法更有意义,你总是可以在类的__init__
中将这些方法添加到那些实例中。这是有道理的。例如:假设我们有Wizard
类,Wizard
实例应该domagic()
方法的唯一时间是magic
参数传递给__init__()
True
。然后我们可以做类似的事情:
class Wizard(object):
def __init__(self, magic = False):
if magic:
def _domagic():
print "DOING MAGIC!"
self.domagic = _domagic
def main():
mage = Wizard(magic = True)
nomage = Wizard(magic = False)
mage.domagic() # prints "DOING MAGIC!"
nomage.domagic() # throws an AttributeError
话虽如此,这段代码确实有点气味 - 现在在向导上调用domagic()之前,你需要知道方法是否定义。我想知道继承heirarchy是否可以稍微改进一点,以使它更优雅。
答案 7 :(得分:0)
虽然我同意这是错误的解决方案,但我“删除”方法的方法是使用引发AttributeError
的属性覆盖该函数。这种方式hasattr
给出了正确的结果,但它没有使用dir
那么麻烦。