我试图将现有Class(不是我的创建)的所有方法都包装到try / except套件中。它可以是任何类,但我在这里使用 pandas.DataFrame 类作为实际示例。
因此,如果调用的方法成功,我们继续前进。但是如果它应该生成一个异常,它会附加到一个列表中供以后检查/发现(尽管下面的例子只是为了简单起见而发布了一个print语句)。
(请注意,调用实例上的方法时可能发生的与数据相关的异常类型尚未知晓;这就是本练习的原因:发现) 的
这post非常有帮助(特别是@martineau Python-3答案),但我在修改它时遇到了麻烦。下面,我预计第二次调用(包装) info()方法来发出打印输出,但遗憾的是,它没有。
#!/usr/bin/env python3
import functools, types, pandas
def method_wrapper(method):
@functools.wraps(method)
def wrapper(*args, **kwargs): #Note: args[0] points to 'self'.
try:
print('Calling: {}.{}()... '.format(args[0].__class__.__name__,
method.__name__))
return method(*args, **kwargs)
except Exception:
print('Exception: %r' % sys.exc_info()) # Something trivial.
#<Actual code would append that exception info to a list>.
return wrapper
class MetaClass(type):
def __new__(mcs, class_name, base_classes, classDict):
newClassDict = {}
for attributeName, attribute in classDict.items():
if type(attribute) == types.FunctionType: # Replace it with a
attribute = method_wrapper(attribute) # decorated version.
newClassDict[attributeName] = attribute
return type.__new__(mcs, class_name, base_classes, newClassDict)
class WrappedDataFrame2(MetaClass('WrappedDataFrame',
(pandas.DataFrame, object,), {}),
metaclass=type):
pass
print('Unwrapped pandas.DataFrame().info():')
pandas.DataFrame().info()
print('\n\nWrapped pandas.DataFrame().info():')
WrappedDataFrame2().info()
print()
输出:
Unwrapped pandas.DataFrame().info():
<class 'pandas.core.frame.DataFrame'>
Index: 0 entries
Empty DataFrame
Wrapped pandas.DataFrame().info(): <-- Missing print statement after this line.
<class '__main__.WrappedDataFrame2'>
Index: 0 entries
Empty WrappedDataFrame2
总之,......
>>> unwrapped_object.someMethod(...)
# Should be mirrored by ...
>>> wrapping_object.someMethod(...)
# Including signature, docstring, etc. (i.e. all attributes); except that it
# executes inside a try/except suite (so I can catch exceptions generically).
答案 0 :(得分:1)
您的元类仅将装饰器应用于作为其实例的类中定义的方法。它不会修饰继承的方法,因为它们不在classDict
。
我不确定是否有一种很好的方法可以让它发挥作用。您可以尝试迭代MRO并包装所有继承的方法以及您自己的方法,但我怀疑如果在开始使用MetaClass
后存在多个级别的继承,您会遇到麻烦(因为每个级别都会装饰已经装饰过的上一课的方法。)
答案 1 :(得分:1)
;-)
事实上,你可能已经很久没有关心了,但万一你(或其他人)做了......
我认为会做你想做的事。我之前从未回答过您的问题,因为我的系统上没有安装pandas
。然而,今天我决定看看是否有一个没有它的解决方法,并创建了一个简单的虚拟模块来模拟它(只在我需要的时候)。这是其中唯一的内容:
mockpandas.py
:
""" Fake pandas module. """
class DataFrame:
def info(self):
print('pandas.DataFrame.info() called')
raise RuntimeError('Exception raised')
下面是代码,它似乎通过实现@ Blckknght的迭代MRO的建议来做你需要的但是忽略了他的答案中提到的可能因为这样做而产生的限制)。它不是很漂亮,但正如我所说,它似乎至少与我创建的模拟pandas
库一起工作。
import functools
import mockpandas as pandas # mock the library
import sys
import traceback
import types
def method_wrapper(method):
@functools.wraps(method)
def wrapper(*args, **kwargs): # Note: args[0] points to 'self'.
try:
print('Calling: {}.{}()... '.format(args[0].__class__.__name__,
method.__name__))
return method(*args, **kwargs)
except Exception:
print('An exception occurred in the wrapped method {}.{}()'.format(
args[0].__class__.__name__, method.__name__))
traceback.print_exc(file=sys.stdout)
# (Actual code would append that exception info to a list)
return wrapper
class MetaClass(type):
def __new__(meta, class_name, base_classes, classDict):
""" See if any of the base classes were created by with_metaclass() function. """
marker = None
for base in base_classes:
if hasattr(base, '_marker'):
marker = getattr(base, '_marker') # remember class name of temp base class
break # quit looking
if class_name == marker: # temporary base class being created by with_metaclass()?
return type.__new__(meta, class_name, base_classes, classDict)
# Temporarily create an unmodified version of class so it's MRO can be used below.
TempClass = type.__new__(meta, 'TempClass', base_classes, classDict)
newClassDict = {}
for cls in TempClass.mro():
for attributeName, attribute in cls.__dict__.items():
if isinstance(attribute, types.FunctionType):
# Convert it to a decorated version.
attribute = method_wrapper(attribute)
newClassDict[attributeName] = attribute
return type.__new__(meta, class_name, base_classes, newClassDict)
def with_metaclass(meta, classname, bases):
""" Create a class with the supplied bases and metaclass, that has been tagged with a
special '_marker' attribute.
"""
return type.__new__(meta, classname, bases, {'_marker': classname})
class WrappedDataFrame2(
with_metaclass(MetaClass, 'WrappedDataFrame', (pandas.DataFrame, object))):
pass
print('Unwrapped pandas.DataFrame().info():')
try:
pandas.DataFrame().info()
except RuntimeError:
print(' RuntimeError exception was raised as expected')
print('\n\nWrapped pandas.DataFrame().info():')
WrappedDataFrame2().info()
输出:
Unwrapped pandas.DataFrame().info():
pandas.DataFrame.info() called
RuntimeError exception was raised as expected
Wrapped pandas.DataFrame().info():
Calling: WrappedDataFrame2.info()...
pandas.DataFrame.info() called
An exception occurred in the wrapped method WrappedDataFrame2.info()
Traceback (most recent call last):
File "test.py", line 16, in wrapper
return method(*args, **kwargs)
File "mockpandas.py", line 9, in info
raise RuntimeError('Exception raised')
RuntimeError: Exception raised
如上所示,method_wrapper()
decoratored version 是被包装类的方法使用。