Python 3.x 在try/except块中包装类的所有可能方法调用
我试图将现有类(不是我创建的)的所有方法包装到try/except套件中。它可以是任何类,但我将使用pandas.DataFrame类作为一个实际示例 因此,如果调用的方法成功,我们只需继续。但是如果它应该生成一个异常,它将被附加到一个列表中,以供以后检查/发现(尽管下面的示例只是为了简单起见发出了一个print语句) (请注意,调用实例上的方法时可能发生的与数据相关的异常类型尚不清楚;这就是本练习的原因:发现) 这非常有用(特别是@martineau Python-3答案),但我在适应它时遇到了困难。下面,我期望对(wrapped)info()方法的第二次调用发出打印输出,但遗憾的是,它没有Python 3.x 在try/except块中包装类的所有可能方法调用,python-3.x,metaprogramming,wrapper,metaclass,python-decorators,Python 3.x,Metaprogramming,Wrapper,Metaclass,Python Decorators,我试图将现有类(不是我创建的)的所有方法包装到try/except套件中。它可以是任何类,但我将使用pandas.DataFrame类作为一个实际示例 因此,如果调用的方法成功,我们只需继续。但是如果它应该生成一个异常,它将被附加到一个列表中,以供以后检查/发现(尽管下面的示例只是为了简单起见发出了一个print语句) (请注意,调用实例上的方法时可能发生的与数据相关的异常类型尚不清楚;这就是本练习的原因:发现) 这非常有用(特别是@martineau Python-3答案),但我在适应它时遇到
#!/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()
您的元类仅将您的装饰器应用于作为其实例的类中定义的方法。它不会修饰继承的方法,因为它们不在
classDict
中
我不确定有没有好办法让它工作。您可以尝试遍历MRO并包装所有继承的方法以及您自己的方法,但我怀疑如果在开始使用元类后有多个继承级别(因为每个级别都将装饰上一个类中已经装饰过的方法),您会遇到麻烦。好久不见<代码>;-)事实上,已经很久了,你可能不再在乎了,但万一你(或其他人)真的在乎了
我想这件事可以满足你的要求。我以前从未回答过你的问题,因为我的系统上没有安装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()
按预期引发RuntimeError异常
已包装的熊猫。数据框().info():
调用:WrappedDataFrame2.info()。。。
调用了pandas.DataFrame.info()
包装方法WrappedDataFrame2.info()中发生异常
回溯(最近一次呼叫最后一次):
包装器中第16行的文件“test.py”
返回方法(*args,**kwargs)
信息中第9行的文件“mockpandas.py”
引发运行时错误(“引发异常”)
运行时错误:引发异常
如上所示,包装类的方法正在使用method\u wrapper()
decorated版本。p.S.如果我延迟对注释或答案的响应,可能是因为我正在尝试提出建议或试图首先理解它。=:)monkey patching Direct pd.DataFrame是解决方案之一吗?monkey patching可能是一个选项。我写《熊猫》的作者也是为了得到他的认可(在StackOverflow之外)。也许他有一些想法,当我和他联系时,我会很高兴地与他分享。试着不走太远的路,但。=:)感谢@Blckknght的反馈。我会更仔细地思考你的答案。嗨@Blckknght。谢谢你有趣的MRO方法回答。关于您提到的有效问题(“多级继承”),您的意思是将来可能对“元类”和/或“WrappedDataFrame2”进行子分类的结果吗?还是其他副作用?(顺便说一句,这里的“元类”是用于一次性/专用的私人用途)。谢谢
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()