类型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:
## ...
有没有办法创建B.f0
& B.f1
通过反映/检查班级A
?
答案 0 :(得分:4)
如果要通过调用预定义类B
上的函数来创建类A
,则可以使用如下所示的函数B = wrap_class(A)
执行wrap_class
:
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)
正如Jürgen指出的那样,这会创建一个类的副本;但是,如果您真的想要保留原始类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
装饰类A
捕获异常。
元类版本较重,但其原理类似:
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代码动态构建B
类的方法(代码对于课程A
,在问题中)。有关这方面的更多信息可以在Chris的Wiki(part 1和parts 2-4)中给出的对元类的简短描述中找到。
答案 1 :(得分:2)
元类是一种选择,但通常很难理解。正如太多的反思 如果在简单的情况下不需要,因为很容易捕获太多(内部)功能。如果包装函数是一个稳定的已知集合,并且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实例的新函数的装饰器。
def B(A):
...
@errorHandler
def f_new(self):
...
这个解决方案使B保持简单,并且非常明确地发生了什么。
答案 2 :(得分:0)
您可以使用__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