Как мы можем последовательно переопределить/перегрузить точечный оператор python `__getattrribute__`?

Предположим, что мы используем python 3.x или новее, а не python 2.x

Python, как и многие языки, имеет оператор точки:

# Create a new instance of the Rectangle class
robby = Rectangle(3, 10)

# INVOKE THE DOT OPERATOR       
x = robby.length 

Оператор точки Python иногда реализуется как __getattribute__.
Следующее эквивалентно x = robby.length:

x = Rectangle.__getattribute__(robby, "length")    

Однако оператор точки не всегда реализуется как __getattribute__.

В Python есть магические методы
магические методы – это любой метод, имя которого начинается и заканчивается двумя символами подчеркивания. символов.
__len__() — это пример магического метода.

Вы можете получить список большинства магических методов Python, выполнив следующий код:

print("\n".join(filter(lambda s: s.startswith("__"), dir(int))))

Результат:

__abs__
__add__
__and__
__bool__
__ceil__
__class__
__delattr__
__dir__
__divmod__
__doc__
__eq__
__float__
[... truncated / abridged ...]
__rtruediv__
__rxor__
__setattr__
__sizeof__
__str__
__sub__
__subclasshook__
__truediv__
__trunc__
__xor__

Предположим, мы пишем класс с именем Rectangle, подкласс object.
Тогда мои попытки переопределить object.__getattribute__ внутри класса Rectangle обычно терпят неудачу.

Ниже показан пример класса, в котором python иногда игнорирует переопределенный оператор точки:

class Klass:
    def __getattribute__(self, attr_name):
        return print

obj = Klass()

obj.append()  # WORKS FINE. `obj.append == print`
obj.delete()  # WORKS FINE. `obj.delete == print`
obj.length    # WORKS FINE
obj.x         # WORKS FINE

# None of the following work, because they
# invoke magic methods.  

# The following line is similar to:
#     x = Klass.__len__(obj)
len(obj)

# obj + 5
#     is similar to:
# x = Klass.__add__(obj, 5)
x = obj + 5

# The following line is similair to:
#     x = Klass.__radd__(obj, 2)
x = 2 + obj

Существует несколько способов переопределить оператор точки Python.
Каков пример одного из способов сделать это, который является читабельным, чистым и непротиворечивым?

Под согласованным я подразумеваю, что наш пользовательский оператор точки вызывается всякий раз, когда в исходном коде используется ., независимо от того, является ли метод магическим или нет.

Я не хочу вручную вводить каждый магический метод на свете.
Я не хочу видеть тысячи строк кода, которые выглядят так:

def __len__(*args, **kwargs):
    return getattr(args[0], "__len__")(*args, **kwargs)

Я понимаю разницу между __getattr__ и __getattribute__
Переопределение __getattribute__ вместо __getattr__ не является проблемой.


person Samuel Muldoon    schedule 02.01.2021    source источник
comment
docs.python.org/3/reference/datamodel.html#special- lookup кажется, что это будет много работы.   -  person Frank Yellin    schedule 03.01.2021


Ответы (1)


__getattribute__ уже делает то, о чем вы буквально просите — переопределение __getattribute__ — это все, что вам нужно для обработки всех случаев использования оператора .. (Строго говоря, Python вернется к __getattr__, если __getattribute__ потерпит неудачу, но вам не нужно об этом беспокоиться, пока вы не реализуете __getattr__.)


Вы говорите, что хотите, чтобы ваш оператор вызывался всякий раз, когда в исходном коде используется ., но len, + и все остальное, о чем вы беспокоитесь, не используйте .. В len(obj), obj + 5 или 2 + obj нет ..

Большинство магических методов поиска не используют доступ к атрибутам. Если вы на самом деле ищете yourobj.__len__ или yourobj.__add__, это пройдет через доступ к атрибуту, и ваш __getattribute__ будет вызван, но когда Python ищет магический метод для реализации языковых функций, он выполняет прямой поиск MRO типа объекта. Оператор . не задействован.

Невозможно переопределить поиск магического метода. Это жестко запрограммированный процесс без хука переопределения. Самое близкое, что вы можете сделать, это переопределить отдельные магические методы для делегирования __getattribute__, но это не то же самое, что переопределить поиск магического метода (или переопределить .), и таким образом легко получить ошибки бесконечной рекурсии.

Если все, что вы действительно хотите сделать, это избежать повторяющихся переопределений отдельных магических методов, вы можете поместить их в декоратор класса или миксин.

person user2357112 supports Monica    schedule 02.01.2021
comment
Что, если мы определим подкласс type с именем sype. sype переопределит значение по умолчанию __mro__ (порядок разрешения методов), определенное в классе type. Если я создам экземпляр класса с именем klass из sype, то, возможно, магические методы будут использовать __getattribute__ - person Samuel Muldoon; 04.01.2021
comment
@SamuelMuldoon: я не уверен, какая идея должна была быть там, но она не работает. Отказ от MRO класса не изменит того, как работает поиск специальных методов. Поиск MRO будет проходить через любой странный MRO, который вы установили, но это все равно будет прямой поиск MRO. (Я переопределил метод mro, который вычисляет MRO, вместо дескриптора __mro__, потому что попытка переопределить __mro__ даже не повлияет на фактический MRO.) - person user2357112 supports Monica; 04.01.2021