Увидел сегодня на русском 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__
К чему это всё? Да просто люблю исследовать Питон. Благо он позволяет легко извлекать из себя и обрабатывать такие штуки, которые из других языков бывает довольно непросто вытащить даже с применением каких-то специальных библиотек. А в Питоне всё это стандартными методами и встроенными библиотеками делается буквально в несколько строк кода.
Спасибо за чтение.











