如何根据条件快速禁用类实例中的所有方法?我天真的解决方案是使用__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()
答案 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
属性的对象。