Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/webpack/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 3.x 在try/except块中包装类的所有可能方法调用_Python 3.x_Metaprogramming_Wrapper_Metaclass_Python Decorators - Fatal编程技术网

Python 3.x 在try/except块中包装类的所有可能方法调用

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答案),但我在适应它时遇到

我试图将现有类(不是我创建的)的所有方法包装到try/except套件中。它可以是任何类,但我将使用pandas.DataFrame类作为一个实际示例

因此,如果调用的方法成功,我们只需继续。但是如果它应该生成一个异常,它将被附加到一个列表中,以供以后检查/发现(尽管下面的示例只是为了简单起见发出了一个print语句)

(请注意,调用实例上的方法时可能发生的与数据相关的异常类型尚不清楚;这就是本练习的原因:发现)

这非常有用(特别是@martineau Python-3答案),但我在适应它时遇到了困难。下面,我期望对(wrapped)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()

您的元类仅将您的装饰器应用于作为其实例的类中定义的方法。它不会修饰继承的方法,因为它们不在
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()