Python内省:方法的自动包装



类型为A的对象,有没有用程序包装类对象的方法?

给定

class A(object):
    def __init__(self):
        ## ..
    def f0(self, a):
        ## ...
    def f1(self, a, b):
        ## ..

我想要另一个包装A的类,比如

class B(object):
    def __init__(self):
        self.a = A()
    def f0(self,a):
        try:
            a.f0(a)
        except (Exception),ex:
            ## ...
    def f1(self, a, b):
        try:
            a.f1(a,b)
        except (Exception),ex:
            ## ...

有没有办法创建CCD_ 1&B.f1通过反射/检查类别A

如果您想通过调用预定义类A上的函数来创建类B,您可以简单地使用如下函数wrap_class来创建B = wrap_class(A)

import copy
def wrap_class(cls):
    'Wraps a class so that exceptions in its methods are caught.'
    # The copy is necessary so that mutable class attributes are not
    # shared between the old class cls and the new class:
    new_cls = copy.deepcopy(cls)
    # vars() is used instead of dir() so that the attributes of base classes
    # are not modified, but one might want to use dir() instead:
    for (attr_name, value) in vars(cls).items():
        if isinstance(value, types.FunctionType):
            setattr(new_cls, attr_name, func_wrapper(value))
    return new_cls
B = wrap_class(A)

正如于尔根所指出的,这创建了类的副本;然而,如果您真的想保留原来的类A(就像原来的问题中建议的那样(,则只需要这样做。如果你不关心A,你可以简单地用一个不执行任何复制的包装器来装饰它,比如:

def wrap_class(cls):
    'Wraps a class so that exceptions in its methods are caught.'
    # vars() is used instead of dir() so that the attributes of base classes
    # are not modified, but one might want to use dir() instead:
    for (attr_name, value) in vars(cls).items():
        if isinstance(value, types.FunctionType):
            setattr(cls, attr_name, func_wrapper(value))
    return cls
@wrap_class
class A(object):
    …  # Original A class, with methods that are not wrapped with exception catching

装饰类B.f00捕获异常。

元类版本更重,但其原理类似:

import types
def func_wrapper(f):
    'Returns a version of function f that prints an error message if an exception is raised.'
    def wrapped_f(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except Exception, ex:
            print "Function", f, "raised", ex
    return wrapped_f
class ExceptionCatcher(type):
    'Metaclass that wraps methods with func_wrapper().'
    def __new__(meta, cname, bases, cdict):
        # cdict contains the attributes of class cname:
        for (attr_name, value) in cdict.items():
            if isinstance(value, types.FunctionType):  # Various attribute types can be wrapped differently
                cdict[attr_name] = func_wrapper(value)
        return super(meta, ExceptionCatcher).__new__(meta, cname, bases, cdict)
class B(object):
    __metaclass__ = ExceptionCatcher  # ExceptionCatcher will be used for creating class A
    class_attr = 42  # Will not be wrapped
    def __init__(self):
        pass
    def f0(self, a):
        return a*10
    def f1(self, a, b):
        1/0  # Raises a division by zero exception!
# Test:
b = B()
print b.f0(3.14)
print b.class_attr
print b.f1(2, 3)

此打印:

31.4
42
Function <function f1 at 0x107812d70> raised integer division or modulo by zero
None

事实上,您想要做的通常是由元类完成的,元类是一个实例为类的类:这是一种基于解析的Python代码(问题中A类的代码(动态构建B类的方法。关于这方面的更多信息可以在Chris的Wiki中(在第1部分和第2-4部分(对元类的简短描述中找到。

元类是一种选择,但通常很难理解。太多的反思也是如此如果在简单的情况下不需要,因为很容易捕获太多(内部(函数。如果封装的函数是一个稳定的已知集合,并且B可能获得其他函数,则可以逐个函数显式委托,并且仍然将错误处理代码保存在一个位置:

class B(object):
    def __init__(self):
        a = A()
        self.f0 = errorHandler(a.f0)  
        self.f1 = errorHandler(a.f1)

如果赋值很多,可以使用getattr/setattr在循环中进行赋值。

errorhandler函数需要返回一个函数,该函数用错误处理代码。

def errorHandler(f):
    def wrapped(*args, **kw):
        try:
            return f(*args, **kw)
        except:
            # log or something
    return wrapped

您还可以在未委托给A实例的新函数上使用errorhandler作为decorator。

def B(A):
    ...
    @errorHandler
    def f_new(self):
        ...

这个解决方案使B保持简单,并且它非常清楚地表明了发生了什么。

你可以用__getattr__:试试它的老派

class B(object):
  def __init__(self):
    self.a = A()
  def __getattr__(self, name):
    a_method = getattr(a, name, None)
    if not callable(a_method):
      raise AttributeError("Unknown attribute %r" % name)
    def wrapper(*args, **kwargs):
      try:
        return a_method(*args, **kwargs)
      except Exception, ex:
        # ...
    return wrapper

或者更新B的格言:

class B(object):
  def __init__(self):
    a = A()
    for attr_name in dir(a):
      attr = getattr(a, attr_name)
      if callable(attr):
        def wrapper(*args, **kwargs):
          try:
            return attr(*args, **kwargs)
          except Exception, ex:
            # ...
        setattr(self, attr_name, wrapper) # or try self.__dict__[x] = y

相关内容

  • 没有找到相关文章

最新更新