在Python中可以从对象(而非类)删除方法吗?
我有一个类,里面有几个方法,但这些方法只有在对象处于特定状态时才有效。我希望在对象不处于合适状态时,这些方法就不再和对象绑定,这样我就能得到类似这样的效果:
>>> 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(method, object)),但是我找不到只针对单个对象删除方法的方法:
>>> 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 中使用)
有什么想法吗?
8 个回答
9
这段话并没有直接回答你的问题,但我觉得解决这个问题的更好方法是不要在你的 wiz
对象上添加或移除成员方法 domagic
。
我建议你在 domagic
方法内部添加一个条件,来检查 wiz
对象的相关状态。然后,只有在状态有效的情况下,才执行 domagic
方法的其他部分;如果状态不对,就输出你选择的错误信息。
def domagic():
if (state != desired_state):
print "You cannot do any magic now!"
return
print "Doing some magic"
[some more commands]
15
你不能从一个类的实例中删除一个类方法,因为这个实例并不“拥有”这个方法。具体来说,如果o
是类Foo
的一个实例,当我调用o.bar()
时,首先会检查o
是否有一个叫bar
的方法。如果没有,就会去检查Foo
这个类。方法并不是直接绑定在实例上的,只有当它们覆盖了实例的类时,才算绑定。
我觉得你现在走的这条路没有什么好处。
4
看起来,默认情况下,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