在Python中可以从对象(而非类)删除方法吗?

15 投票
8 回答
10212 浏览
提问于 2025-04-15 15:42

我有一个类,里面有几个方法,但这些方法只有在对象处于特定状态时才有效。我希望在对象不处于合适状态时,这些方法就不再和对象绑定,这样我就能得到类似这样的效果:

>>> 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

撰写回答