是否可以从python中的对象(而不是类)中删除方法?

时间:2009-11-05 22:29:28

标签: 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(方法,对象)),但我看不到任何方法只删除单个对象的方法:

>>> 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 个答案:

答案 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解决问题的典型例子:

  

此模式用于计算机   编程来表示状态   一个东西。这是一个干净的方式   对象部分改变其类型   运行时。

如果向导具有以下属性,我会考虑使用此模式:

  1. 向导对象可以包含少量定义良好的状态。如果对象可以存在快速增长/变化的状态集,则此模式可能会出现问题。关于可能过多的州的数量并不难,但我建议如果你不确定使用这种模式,那么如果维护变得太多,那就去寻找替代方案。
  2. 根据州的不同,有多种方法需要做不同的事情。在这种情况下,您可能会为每个方法结束一系列长 if / elif 语句。这使得每个状态的逻辑分散在一个整体类中,使得难以维护。有一个代表每个状态的类封装了它自己单元中每个逻辑状态的规则,这使得更容易理解每​​个状态如何在它自己上运行。此外,您可以独立于状态对象的使用者单元测试每个状态。
  3. 有问题的是相当具体的,并且可以很容易地相互区分。这将使每个州分成自己的类更加容易。

答案 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那么麻烦。