开发者

Disable class instance methods

How can I quickly disable all methods in a class instance based on a condition? My naive solution is to override using the __getattr__ but this is not called when the function name exists already.

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


The equivalent of what you want to do is actually to override __getattribute__, which is going to be called for every attribute access. Besides it being very slow, take care: by definition of every, that includes e.g. the call to self.isValid within __getattribute__'s own body, so you'll have to use some circuitous route to access that attribute (type(self).isValid(self) should work, for example, as it gets the attribute from the class, not from the instance).

This points to a horrible terminological confusion: this is not disabling "method from a class", but from an instance, and in particular has nothing to do with classmethods. If you do want to work in a similar way on a class basis, rather than an instance basis, you'll need to make a custom metaclass and override __getattribute__ on the metaclass (that's the one that's called when you access attributes on the class -- as you're asking in your title and text -- rather than on the instance -- as you in fact appear to be doing, which is by far the more normal and usual case).

Edit: a completely different approach might be to use a peculiarly Pythonic pathway to implementing the State design pattern: class-switching. E.g.:

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

As long as you can call setValid appropriately, so that the object's __class__ is switched appropriately, this is very fast and simple -- essentially, the object's __class__ is where all the object's methods are found, so by switching it you switch, en masse, the set of methods that exist on the object at a given time. However, this does not work if you absolutely insist that validity checking must be performed "just in time", i.e. at the very instant the object's method is being looked up.

An intermediate approach between this and the __getattribute__ one would be to introduce an extra level of indirection (which is popularly held to be the solution to all problems;-), along the lines of:

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)

This is more idiomatic than __getattribute__ because it relies on the fact that __getattr__ is only called for attributes that aren't found in other ways -- so the object can hold normal state (data) in its __dict__, and that will be accessed without any big overhead; only method calls pay the extra overhead of indiretion. The _Valid class instances can keep some or all state in their respective self._actualobject, if any of the state needs to stay accessible on invalid objects (so that the invalid state disable methods, but not data attributes access; it's not clear from your Q if that's needed, but it's a free extra possibility offered by this approach). This idiom is less error-prone than __getattribute__, since state can be accessed more directly in the methods (without triggering validity checks).

As presented, the solution creates a circular reference loop, which may impose a bit of overhead in terms of garbage collection. If that's a problem in your application, use the weakref module from the standard Python library, of course -- that module is generally the simplest way to remove circular loops of references, if and when they're a problem. (E.g., make the _actualobject attribute of _Valid class instances a weak reference to the object that holds that instance as its _themethods attribute).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜