gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 00:20:33 25 4
gpt4 key购买 nike

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的 future 版本中引入新的魔术方法,它将不再起作用
  • 第一行,class MagicOverrider:,抛出TypeError: 'function' object is not iterable

最佳答案

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

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

可以做的就是让 magicmethods 本身在运行时触发 __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 - 我们如何强制为魔术方法(特殊方法)调用 getattribute() ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48966182/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com