如何强制魔术方法(特殊方法)调用getattribute()? - python

python documentation指出查找特殊方法时可能会绕过__getattribute__。这是通过语言语法或内置函数进行隐式调用的结果。

例如,

elem = container[0]

与以下内容不同:

elem = container.__getattribute__('__getitem__')[0]

下面是另一个示例:

class WrappedList:
    def __init__(self):
        object.__setattr__(self, 'interal_list', ['apple', 'pear', 'orange'])

    def __getattribute__(self, attr_name):
        interal_list = object.__getattribute__(self, 'interal_list')
        attr = getattr(interal_list, attr_name)
        return attr

wl = WrappedList()

print("\nSTART TEST 01 ------------------------")
try:
    print(wl[0]) # throws TypeError: 'WrappedList' object does not support indexing
except TypeError as e:
    print(e)

print("\nSTART TEST 02 ------------------------")
try:
    getitem = getattr(wl, '__getitem__')
    print(getitem(0)) # works just fine
except TypeError as e:
    print(e)

我想编写一个名为MagicOverrider之类的类,其中从MagicOverrider继承的任何类都始终调用__getattribute__,而不是绕开它。我的问题是我们该怎么做?

我尝试了以下方法:

class MagicOverrider:

    def __call__(self, *args, **kwargs):
        f = getattr(self, '__call__')
        return f(*args, **kwargs)

    def __iter__(self, *args, **kwargs):
        f = getattr(self, '__iter__')
        return f(*args, **kwargs)

    def __getitem__(self, *args, **kwargs):
        f = getattr(self, '__getitem__')
        return f(*args, **kwargs)

    def __setitem__(self, *args, **kwargs):
        f = getattr(self, '__setitem__')
        return f(*args, **kwargs)

    def __add__(self, *args, **kwargs):
        f = getattr(self, '__add__')
        return f(*args, **kwargs)

    def __sub__(self, *args, **kwargs):
        f = getattr(self, '__sub__')
        return f(*args, **kwargs)

    def __mul__(self, *args, **kwargs):
        f = getattr(self, '__mul__')
        return f(*args, **kwargs)

    def __truediv__(self, *args, **kwargs):
        f = getattr(self, '__truediv__')
        return f(*args, **kwargs)

    def __floordiv__(self, *args, **kwargs):
        f = getattr(self, '__floordiv__')
        return f(*args, **kwargs)

    def __mod__(self, *args, **kwargs):
        f = getattr(self, '__mod__')
        return f(*args, **kwargs)

    def __divmod__(self, *args, **kwargs):
        f = getattr(self, '__divmod__')
        return f(*args, **kwargs)

    def __pow__(self, *args, **kwargs):
        f = getattr(self, '__pow__')
        return f(*args, **kwargs)

    def __lshift__(self, *args, **kwargs):
        f = getattr(self, '__lshift__')
        return f(*args, **kwargs)

    def __rshift__(self, *args, **kwargs):
        f = getattr(self, '__rshift__')
        return f(*args, **kwargs)

    def __and__(self, *args, **kwargs):
        f = getattr(self, '__and__')
        return f(*args, **kwargs)

    def __xor__(self, *args, **kwargs):
        f = getattr(self, '__xor__')
        return f(*args, **kwargs)

    def __or__(self, *args, **kwargs):
        f = getattr(self, '__or__')
        return f(*args, **kwargs)

    def __radd__(self, *args, **kwargs):
        f = getattr(self, '__radd__')
        return f(*args, **kwargs)

    def __rsub__(self, *args, **kwargs):
        f = getattr(self, '__rsub__')
        return f(*args, **kwargs)

    def __rmul__(self, *args, **kwargs):
        f = getattr(self, '__rmul__')
        return f(*args, **kwargs)

    def __rtruediv__(self, *args, **kwargs):
        f = getattr(self, '__rtruediv__')
        return f(*args, **kwargs)

    def __rfloordiv__(self, *args, **kwargs):
        f = getattr(self, '__rfloordiv__')
        return f(*args, **kwargs)

    def __rmod__(self, *args, **kwargs):
        f = getattr(self, '__rmod__')
        return f(*args, **kwargs)

    def __rdivmod__(self, *args, **kwargs):
        f = getattr(self, '__rdivmod__')
        return f(*args, **kwargs)

    def __rpow__(self, *args, **kwargs):
        f = getattr(self, '__rpow__')
        return f(*args, **kwargs)

    def __rlshift__(self, *args, **kwargs):
        f = getattr(self, '__rlshift__')
        return f(*args, **kwargs)

    def __rrshift__(self, *args, **kwargs):
        f = getattr(self, '__rrshift__')
        return f(*args, **kwargs)

    def __rand__(self, *args, **kwargs):
        f = getattr(self, '__rand__')
        return f(*args, **kwargs)

    def __rxor__(self, *args, **kwargs):
        f = getattr(self, '__rxor__')
        return f(*args, **kwargs)

    def __neg__(self, *args, **kwargs):
        f = getattr(self, '__neg__')
        return f(*args, **kwargs)

    def __pos__(self, *args, **kwargs):
        f = getattr(self, '__pos__')
        return f(*args, **kwargs)

    def __abs__(self, *args, **kwargs):
        f = getattr(self, '__abs__')
        return f(*args, **kwargs)

    def __invert__(self, *args, **kwargs):
        f = getattr(self, '__invert__')
        return f(*args, **kwargs)

    def __complex__(self, *args, **kwargs):
        f = getattr(self, '__complex__')
        return f(*args, **kwargs)

    def __int__(self, *args, **kwargs):
        f = getattr(self, '__int__')
        return f(*args, **kwargs)

    def __float__(self, *args, **kwargs):
        f = getattr(self, '__float__')
        return f(*args, **kwargs)

    def __round__(self, *args, **kwargs):
        f = getattr(self, '__round__')
        return f(*args, **kwargs)

    def __index__(self, *args, **kwargs):
        f = getattr(self, '__index__')
        return f(*args, **kwargs)

    def __eq__(self, *args, **kwargs):
        f = getattr(self, '__eq__')
        return f(*args, **kwargs)

    def __ne__(self, *args, **kwargs):
        f = getattr(self, '__ne__')
        return f(*args, **kwargs)

    def __lt__(self, *args, **kwargs):
        f = getattr(self, '__lt__')
        return f(*args, **kwargs)

    def __le__(self, *args, **kwargs):
        f = getattr(self, '__le__')
        return f(*args, **kwargs)

    def __gt__(self, *args, **kwargs):
        f = getattr(self, '__gt__')
        return f(*args, **kwargs)

    def __ge__(self, *args, **kwargs):
        f = getattr(self, '__ge__')
        return f(*args, **kwargs)

    def __bool__(self, *args, **kwargs):
        f = getattr(self, '__bool__')
        return f(*args, **kwargs)

    def __new__(self, *args, **kwargs):
        f = getattr(self, '__new__')
        return f(*args, **kwargs)

    def __del__(self, *args, **kwargs):
        f = getattr(self, '__del__')
        return f(*args, **kwargs)

    def __slots__(self, *args, **kwargs):
        f = getattr(self, '__slots__')
        return f(*args, **kwargs)

    def __hash__(self, *args, **kwargs):
        f = getattr(self, '__hash__')
        return f(*args, **kwargs)

    def __instancecheck__(self, *args, **kwargs):
        f = getattr(self, '__instancecheck__')
        return f(*args, **kwargs)

    def __subclasscheck__(self, *args, **kwargs):
        f = getattr(self, '__subclasscheck__')
        return f(*args, **kwargs)

    def __subclasshook__(self, *args, **kwargs):
        f = getattr(self, '__subclasshook__')
        return f(*args, **kwargs)

    def __ror__(self, *args, **kwargs):
        f = getattr(self, '__ror__')
        return f(*args, **kwargs)

    def __iadd__(self, *args, **kwargs):
        f = getattr(self, '__iadd__')
        return f(*args, **kwargs)

    def __isub__(self, *args, **kwargs):
        f = getattr(self, '__isub__')
        return f(*args, **kwargs)

    def __imul__(self, *args, **kwargs):
        f = getattr(self, '__imul__')
        return f(*args, **kwargs)

    def __itruediv__(self, *args, **kwargs):
        f = getattr(self, '__itruediv__')
        return f(*args, **kwargs)

    def __ifloordiv__(self, *args, **kwargs):
        f = getattr(self, '__ifloordiv__')
        return f(*args, **kwargs)

    def __imod__(self, *args, **kwargs):
        f = getattr(self, '__imod__')
        return f(*args, **kwargs)

    def __ipow__(self, *args, **kwargs):
        f = getattr(self, '__ipow__')
        return f(*args, **kwargs)

    def __ilshift__(self, *args, **kwargs):
        f = getattr(self, '__ilshift__')
        return f(*args, **kwargs)

    def __irshift__(self, *args, **kwargs):
        f = getattr(self, '__irshift__')
        return f(*args, **kwargs)

    def __iand__(self, *args, **kwargs):
        f = getattr(self, '__iand__')
        return f(*args, **kwargs)

    def __ixor__(self, *args, **kwargs):
        f = getattr(self, '__ixor__')
        return f(*args, **kwargs)

    def __repr__(self, *args, **kwargs):
        f = getattr(self, '__repr__')
        return f(*args, **kwargs)

    def __str__(self, *args, **kwargs):
        f = getattr(self, '__str__')
        return f(*args, **kwargs)

    def __cmp__(self, *args, **kwargs):
        f = getattr(self, '__cmp__')
        return f(*args, **kwargs)

    def __rcmp__(self, *args, **kwargs):
        f = getattr(self, '__rcmp__')
        return f(*args, **kwargs)

    def __nonzero__(self, *args, **kwargs):
        f = getattr(self, '__nonzero__')
        return f(*args, **kwargs)

    def __unicode__(self, *args, **kwargs):
        f = getattr(self, '__unicode__')
        return f(*args, **kwargs)

但是,我的解决方案至少有两个问题:

如果在未来的python版本中引入了新的魔术方法,它将不再起作用
第一行class MagicOverrider:引发TypeError: 'function' object is not iterable

参考方案

这很棘手。
因为当通过语言结构触发魔术方法时,Python不会通过正常情况下(即,使用__getattribute__等)使用的常规属性检索路径:相反,只要将特殊方法分配给类,它会在类本身的二进制数据结构中标记(由Python解释器中的C代码完成)。这样做是为了使这种用法是捷径-否则,仅仅为了获得执行正确方法(例如加法或项检索)的代码太多。而且,很容易出现一些无限递归循环。

所以-魔术方法总是直接从bt Python检索-没有__getattribute__

相反,可以做的是让魔术方法自己在运行时触发__getattribute__。如果他们得到与自己不同的结果,他们将其称为。只是要注意避免无限递归。

至于潜在的魔术方法:由于无论如何都需要一个元类,因此在创建将强制使用__getattribute__的类时,让元类为所需的类包装所有魔术方法就可以了。

下面的代码可以做到这一点,并包括一个示例类,该类将临时包装放在__getitem__上:

from functools import wraps
from threading import local as thread_local
from types import MethodType

def wrap(name, method):
    local_flag = thread_local()
    @wraps(method)
    def wrapper(*args, **kw):
        local_method = method
        if not getattr(local_flag, "running", False) and args and not isinstance(args[0], type):
            local_flag.running = True
            # trigger __getattribute__:
            self = args[0]
            cls = self.__class__
            retrieved = cls.__getattribute__(self, name)
            if not retrieved is wrapper:
                local_method =  retrieved
            if isinstance(local_method, MethodType):
                args = args[1:]
        result = local_method(*args, **kw)
        local_flag.running = False
        return result
    wrapper._wrapped = True
    return wrapper


class MetaOverrider(type):
    def __init__(cls, name, bases, namespace, **kwd):
        super().__init__(name, bases, namespace, **kwd)

        for name in dir(cls):
            if not (name.startswith("__")  and name.endswith("__")):
                continue
            if name in ("__getattribute__", "__class__", "__init__"):
                continue
            magic_method = getattr(cls, name)
            if not callable(magic_method) or getattr(magic_method, "_wrapped", False):
                continue
            setattr(cls, name, wrap(name, magic_method))



class TestOverriding(list, metaclass=MetaOverrider):
    def __getattribute__(self, attrname):
        attr = super().__getattribute__(attrname)
        if attrname == "__getitem__":
            original = attr
            def printergetitem(self, index):
                print("Getting ", index)
                return original(index)
            attr = printergetitem
        return attr

它可以与任何魔术方法一起使用-但是,当然,如果在创建类之后将魔术方法分配给类本身,它将遮蔽所使用的包装方法。但是对于__getattribute__本身添加的任何魔术包装,它都应该起作用。

Python:使用* args ** kwargs传递可选的命名变量 - python

我有一个自定义的dict类(collections.MutablMapping),实际的对象要复杂一些,但是我遇到的问题却很简单,如何将自定义参数传递给__init__ *args之外的**kwargs方法,转到dict()class TestDict(collections.MutableMapping): def __init__(self, *args…

在返回'Response'(Python)中传递多个参数 - python

我在Angular工作,正在使用Http请求和响应。是否可以在“响应”中发送多个参数。角度文件:this.http.get("api/agent/applicationaware").subscribe((data:any)... python文件:def get(request): ... return Response(seriali…

Python exchangelib在子文件夹中读取邮件 - python

我想从Outlook邮箱的子文件夹中读取邮件。Inbox ├──myfolder 我可以使用account.inbox.all()阅读收件箱,但我想阅读myfolder中的邮件我尝试了此页面folder部分中的内容,但无法正确完成https://pypi.python.org/pypi/exchangelib/ 参考方案 您需要首先掌握Folder的myfo…

R'relaimpo'软件包的Python端口 - python

我需要计算Lindeman-Merenda-Gold(LMG)分数,以进行回归分析。我发现R语言的relaimpo包下有该文件。不幸的是,我对R没有任何经验。我检查了互联网,但找不到。这个程序包有python端口吗?如果不存在,是否可以通过python使用该包? python参考方案 最近,我遇到了pingouin库。

argparse参数顺序 - python

我有一点问题。我使用argparse解析我的参数,并且运行良好。要拥有args,我这样做:p_args = parser.parse_args(argv) args = dict(p_args._get_kwargs()) 但是p_args的问题是我不知道如何根据命令行中的位置来排列这些参数,因为这是命令。那么,是否有可能在命令行中将参数放在元组/列表/有序…