Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/351.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';s";“魔术”;新样式类中的方法?_Python_Python 3.x - Fatal编程技术网

如何拦截对python';s";“魔术”;新样式类中的方法?

如何拦截对python';s";“魔术”;新样式类中的方法?,python,python-3.x,Python,Python 3.x,我试图在新样式的类中拦截对python的双下划线魔术方法的调用。这是一个微不足道的例子,但它表明了以下意图: class ShowMeList(object): def __init__(self, it): self._data = list(it) def __getattr__(self, name): attr = object.__getattribute__(self._data, name) if callable(

我试图在新样式的类中拦截对python的双下划线魔术方法的调用。这是一个微不足道的例子,但它表明了以下意图:

class ShowMeList(object):
    def __init__(self, it):
        self._data = list(it)

    def __getattr__(self, name):
        attr = object.__getattribute__(self._data, name)
        if callable(attr):
            def wrapper(*a, **kw):
                print "before the call"
                result = attr(*a, **kw)
                print "after the call"
                return result
            return wrapper
        return attr
如果我在列表中使用代理对象,我会得到非魔法方法的预期行为,但我的包装器函数永远不会被魔法方法调用

>>> l = ShowMeList(range(8))

>>> l #call to __repr__
<__main__.ShowMeList object at 0x9640eac>

>>> l.append(9)
before the call
after the call

>> len(l._data)
9

如何使用新样式类完成此截取?

作为的答案(另请参见),使用
\uuuu getattr\uuuuu
\uu getattribute\uuuuu
修改对“magic”方法的访问对于新样式类来说是不可能的。此限制使解释器速度更快。

使用
\uuuuu getattr\uuuuu
\uuuuu getattribute\uuuuuu
是类响应获取属性的最后一个资源

考虑以下几点:

>>> class C:
    x = 1
    def __init__(self):
        self.y = 2
    def __getattr__(self, attr):
        print(attr)

>>> c = C()
>>> c.x
1
>>> c.y
2
>>> c.z
z
\uuuu getattr\uuuu
方法只有在其他方法无效时才被调用(它对运算符无效,您可以阅读相关内容)

在您的示例中,
对象
类中已经定义了
报告
和许多其他神奇方法


我们可以做的一件事是定义这些神奇的方法,然后调用
\uu getattr\uu
方法。请检查我提出的另一个问题及其答案()以查看执行此操作的代码。

从文档中剪切并复制:

对于旧式类,特殊方法总是以与任何其他方法或属性完全相同的方式查找


对于新样式的类,只有在对象的类型上定义了特殊方法的隐式调用,而不是在对象的实例字典中定义的情况下,才能保证其正确工作。

出于性能原因,Python总是在类(和父类)中查找
\uuuu dict\uuuu
用于魔法方法,不使用常规属性查找机制。解决方法是使用元类在创建类时自动为魔术方法添加代理;例如,我使用这种技术来避免通过包装类的方法编写样板调用

class Wrapper(object):
    """Wrapper class that provides proxy access to an instance of some
       internal instance."""

    __wraps__  = None
    __ignore__ = "class mro new init setattr getattr getattribute"

    def __init__(self, obj):
        if self.__wraps__ is None:
            raise TypeError("base class Wrapper may not be instantiated")
        elif isinstance(obj, self.__wraps__):
            self._obj = obj
        else:
            raise ValueError("wrapped object must be of %s" % self.__wraps__)

    # provide proxy access to regular attributes of wrapped object
    def __getattr__(self, name):
        return getattr(self._obj, name)

    # create proxies for wrapped object's double-underscore attributes
    class __metaclass__(type):
        def __init__(cls, name, bases, dct):

            def make_proxy(name):
                def proxy(self, *args):
                    return getattr(self._obj, name)
                return proxy

            type.__init__(cls, name, bases, dct)
            if cls.__wraps__:
                ignore = set("__%s__" % n for n in cls.__ignore__.split())
                for name in dir(cls.__wraps__):
                    if name.startswith("__"):
                        if name not in ignore and name not in dct:
                            setattr(cls, name, property(make_proxy(name)))
用法:

class DictWrapper(Wrapper):
    __wraps__ = dict

wrapped_dict = DictWrapper(dict(a=1, b=2, c=3))

# make sure it worked....
assert "b" in wrapped_dict                        # __contains__
assert wrapped_dict == dict(a=1, b=2, c=3)        # __eq__
assert "'a': 1" in str(wrapped_dict)              # __str__
assert wrapped_dict.__doc__.startswith("dict()")  # __doc__

通过截取双下划线方法,您真正想做什么?或者只是出于好奇?我总是在这里保留一个所有神奇方法的列表:(已生成。因此它适用于python 2和3)实际上,您要做的似乎是拦截对一个新样式类实例的神奇方法的调用——这仍然是一个好问题,然而,这个答案是100%正确的,但没有解释任何东西,完全没有帮助。对不起。贝尔实验室的一个老笑话:一名飞行员在浓雾中迷路,看到一栋办公楼,大喊“我在哪里?”,得到答案“在飞机上”,然后降落在。。。艾伦镇。他怎么知道的?“答案100%正确…”谢谢。我发现充实您的答案很有帮助。@kindall我删除了看起来像一行孤立的代码,但您可能需要再次检查示例是否没有丢失预期的功能。在循环执行
dir()之前执行
ignore.update(dct)
有什么问题吗
然后将最后两个
if
语句压缩为一个?对我来说感觉更清晰,但可能有一些我在缺乏经验的情况下没有预料到的意外后果。
class DictWrapper(Wrapper):
    __wraps__ = dict

wrapped_dict = DictWrapper(dict(a=1, b=2, c=3))

# make sure it worked....
assert "b" in wrapped_dict                        # __contains__
assert wrapped_dict == dict(a=1, b=2, c=3)        # __eq__
assert "'a': 1" in str(wrapped_dict)              # __str__
assert wrapped_dict.__doc__.startswith("dict()")  # __doc__