Увидел сегодня на русском Stackoverflow один не отвеченный вопрос про то, как понять, какой метод с двойным подчёркиванием будет вызван, если в выражении будет использован тот или иной оператор.
Не очень было понятно, как можно извлечь эту информацию из самого Питона, не заглядывая в готовые таблицы соответствия (это было бы скучно). Тем более, что на самом деле Питон много чего оптимизирует, и на самом деле при сложении двух сущностей обычно не будет вызван метод __add__
, а в байткоде сгенерируется вызов BINARY_ADD
, в чём можно легко убедиться с помощью модуля dis
(если речь не о самописном классе с перегруженным методом __add__
, а о каких-то стандартных объектах Питона). Например, сложим два списка через +
и через __add__
и посмотрим, какой байткод будет сгенерирован.
import dis
dis.dis("first_list = [1, 2]; second_list = [3, 4]; final_list = first_list + second_list")
# Только самое существенное в выводе:
# 16 LOAD_NAME 0 (first_list)
# 18 LOAD_NAME 1 (second_list)
# 20 BINARY_ADD
dis.dis("first_list = [1, 2]; second_list = [3, 4]; final_list = first_list.__add__(second_list)")
# Опять же самая суть:
# 16 LOAD_NAME 0 (first_list)
# 18 LOAD_METHOD 2 (__add__)
# 20 LOAD_NAME 1 (second_list)
# 22 CALL_METHOD 1
И тут я вдруг вспомнил про стандартный модуль operator
, в который зашиты все возможные операторы Питона именно в виде именованных методов как с двойным подчёркиванием, так и без оного. После небольшого исследования оказалось, что в нём каждый такой метод имеет строку документации, в которой таки написано что-то типа "Same as a + b." ("Тоже самое, что a+b.")
То есть мы хотя бы можем извлечь табличку соответствия операторов и этих методов прямо изнутри самого Питона. В результате был написан следующий код.
# Модуль operator содержит методы, аналогичные встроенным методам Питона и методам классов Питона
import operator
import re
# Будем искать в докстрингах методов фразу "Same as" ("То же, что и")
rx = re.compile('Same as (.*)')
# Перебираем имена модуля operator
for name in dir(operator):
# Нас интересуют только имеющие двойное подчёркивание в названии
if '__' in name:
# Берём аттрибут модуля operator с таким именем
attr = getattr(operator, name)
# Читаем его docstring и ищем там фразу (см. выше)
descr = rx.findall(attr.__doc__)
# Если фраза нашлась, то она там одна и заканчивается она точкой, которая нам не нужна
if descr:
print(f'{descr[0][:-1]} -> {name}')
Получилось такое соответствие.
abs(a) -> __abs__
a + b -> __add__
a & b -> __and__
a + b, for a and b sequences -> __concat__
b in a (note reversed operands) -> __contains__
del a[b] -> __delitem__
a == b -> __eq__
a // b -> __floordiv__
a >= b -> __ge__
a[b] -> __getitem__
a > b -> __gt__
a += b -> __iadd__
a &= b -> __iand__
a += b, for a and b sequences -> __iconcat__
a //= b -> __ifloordiv__
a <<= b -> __ilshift__
a @= b -> __imatmul__
a %= b -> __imod__
a *= b -> __imul__
a.__index__( -> __index__
~a -> __inv__
~a -> __invert__
a |= b -> __ior__
a **= b -> __ipow__
a >>= b -> __irshift__
a -= b -> __isub__
a /= b -> __itruediv__
a ^= b -> __ixor__
a <= b -> __le__
a << b -> __lshift__
a < b -> __lt__
a @ b -> __matmul__
a % b -> __mod__
a * b -> __mul__
a != b -> __ne__
-a -> __neg__
not a -> __not__
a | b -> __or__
+a -> __pos__
a ** b -> __pow__
a >> b -> __rshift__
a[b] = c -> __setitem__
a - b -> __sub__
a / b -> __truediv__
a ^ b -> __xor__
К чему это всё? Да просто люблю исследовать Питон. Благо он позволяет легко извлекать из себя и обрабатывать такие штуки, которые из других языков бывает довольно непросто вытащить даже с применением каких-то специальных библиотек. А в Питоне всё это стандартными методами и встроенными библиотеками делается буквально в несколько строк кода.
Спасибо за чтение.