禁用类实例方法

时间:2010-02-18 15:34:12

标签: python

如何根据条件快速禁用类实例中的所有方法?我天真的解决方案是使用__getattr__覆盖,但是当函数名已经存在时,不会调用它。

class my():
    def method1(self):
        print 'method1'
    def method2(self):
        print 'method2'
    def __getattr__(self, name):
        print 'Fetching '+str(name)
        if self.isValid():
            return getattr(self, name)
    def isValid(self):
        return False
if __name__ == '__main__':
    m=my()
    m.method1()

1 个答案:

答案 0 :(得分:5)

相当于您要执行的操作实际上是覆盖__getattribute__,这将被每个属性访问调用。除了非常缓慢之外,请注意:根据每个的定义,包括例如在self.isValid自己的正文中调用__getattribute__,因此您必须使用一些迂回路线来访问该属性(type(self).isValid(self)应该有效,例如,当它获取属性时来自班级,而不是来自实例)。

这指出了一个可怕的术语混淆:这是禁用“类中的方法”,而是来自实例,特别是与{无关> {1}}。如果你想在类的基础上以类似的方式工作,而不是以实例为基础,你需要创建一个自定义元类并覆盖元类上的classmethods (这就是当您在课程上访问属性时调用的那个 - 正如您在标题和文字中询问的那样 - 而不是实例上的 < - >,因为您实际上已经出现了要这样做,这是迄今为止更正常和通常的情况)。

编辑:一种完全不同的方法可能是使用一种特殊的Pythonic途径来实现__getattribute__设计模式:类切换。 E.g:

State

只要你可以适当地调用class _NotValid(object): def isValid(self): return False def setValid(self, yesno): if yesno: self.__class__ = TheGoodOne class TheGoodOne(object): def isValid(self): return True def setValid(self, yesno): if not yesno: self.__class__ = _NotValid # write all other methods here ,以便对象的setValid被适当地切换,这是非常快速和简单的 - 基本上,对象的__class__是所有的找到了对象的方法,因此通过切换它,您可以在一定时间内切换对象上存在的方法集。但是,如果您绝对坚持必须“及时”执行有效性检查,即在查找对象方法的那一刻,这不起作用。

这个和__class__之间的中间方法是引入一个额外的间接水平(这通常被认为是所有问题的解决方案;-),如下所示:

__getattribute__

这比class _Valid(object): def __init__(self, actualobject): self._actualobject = actualobject # all actual methods go here # keeping state in self._actualobject class Wrapit(object): def __init__(self): self._themethods = _Valid(self) def isValid(self): # whatever logic you want # (DON'T call other self. methods!-) return False def __getattr__(self, n): if self.isValid(): return getattr(self._themethods, n) raise AttributeError(n) 更惯用,因为它依赖于__getattribute__只调用其他方式找不到的属性的事实 - 因此对象可以保持正常状态(数据)在__getattr__中,可以在没有任何大开销的情况下访问它;只有方法调用才能支付额外的间接费用。如果任何状态需要在无效对象上保持可访问,则__dict__类实例可以将部分或全部状态保留在各自的_Valid中(以便无效状态禁用方法,但不能访问数据属性;从你的Q中不清楚是否需要,但这种方法提供了免费的额外可能性。这个成语比self._actualobject更不容易出错,因为可以在方法中更直接地访问状态(不触发有效性检查)。

如上所述,该解决方案创建了一个循环引用循环,这可能会在垃圾收集方面产生一些开销。如果这是您的应用程序中的问题,当然使用标准Python库中的weakref模块 - 该模块通常是删除引用循环循环的最简单方法,如果它们出现问题。 (例如,将__getattribute__类实例的_actualobject属性设为引用该实例作为其_Valid属性的对象。