Если вы хотите создать класс 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)
Как отметил Юрген, это создает копию класса; однако это необходимо только в том случае, если вы действительно хотите сохранить свой оригинальный класс 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
То, что вы хотите сделать, на самом деле обычно делается метаклассом, который является классом, экземплярами которого является класс: это способ динамического создания класса B
на основе его разобранного кода Python (кода для класса A
, в вопросе). Дополнительную информацию об этом можно найти в хорошем, коротком описании метаклассов, приведенном в вики Криса (в часть 1 и части 2-4 ).