Полиморфизм помогает максимально использовать возможности языка и решать сложные задачи. Особенно полезно понимать его реализацию внутри языка. Это поможет сделать программы более эффективными и надежными.
В этом уроке мы погрузимся во внутренние механизмы Python и исследуем, как работает полиморфизм с помощью динамической диспетчеризации.
Понимание динамической диспетчеризации
По функциям всегда можно сказать, где они определены. Для этого нужно посмотреть на модули, из которых их импортировали.
С методами все сложнее. Когда мы видим вызов метода, нельзя сразу сказать, откуда он пришел. Это зависит от типа объекта, у которого он вызван. Полиморфизм подразумевает подмену объектов, а значит одна и та же строчка кода может вызывать разные методы (но имеющие одинаковые имена), в зависимости от пришедшего объекта.
Полиморфизм в Python реализуется с помощью динамической диспетчеризации. Это механизм, который занимается выбором необходимой реализации метода.
Рассмотрим следующий пример:
def print_name(obj):
print(obj.get_name())
print_name(User({"name": "Mike"}))
Здесь показана функция, которая ожидает, что на вход ей передается объект с методом get_name()
и печатает его на экран.
Технически этот код можно представить так:
class User:
def __init__(self, data):
self.name = data["name"]
def get_name(self):
return self.name
def print_name(obj):
print(obj.get_name())
print_name(User({"name": "Mike"}))
В этом примере User({'name': 'Mike'})
создает объект. В Python вызов метода запускает механизм диспетчеризации, который определяет класс данного объекта, затем проверяет список методов этого класса и ищет среди них get_name()
.
Работа диспетчера
Разберем, что происходит при вызове метода:
class User:
def __init__(self, name):
self.name = name
def get_name(self):
return self.name
def call_method(self, method_name, *args):
methods = getattr(self, "__class__").__dict__
method = methods.get(method_name)
if method:
return method(self, *args)
raise Exception("No method error")
call_method(User("Mike"), "get_name")
Этот код содержит три важные детали:
- Методы — это логическое понятие. Внутри языка это обычные функции
- Список классов и методов хранится в структуре данных, называемой словарем
obj
— это исходный объект, который передается первым параметром. Мы получили его, когда делалиUser({'name': 'Mike'})
Важно отметить, что в Python self
является явным аргументом для методов. Это отличительная черта Python по сравнению с некоторыми другими языками, такими как PHP.
Подобная диспетчеризация называется одиночной. Она опирается на один параметр структуры — ее класс. В других языках, которые тоже имеют поддержку ООП, но без классов, встречается мультидиспетчеризация.
В этих языках функцию-диспетчер можно написать самому под конкретную полиморфную задачу. Такой подход более мощный и позволяет получить большее меньшим количеством кода. Например, к таким языкам относится Clojure.
Выводы
В этом уроке мы углубились в понимание полиморфизма и того, как он реализуется в Python с помощью механизма динамической диспетчеризации. Мы посмотрели, как Python определяет, какой метод следует вызвать для определенного объекта, и научились представлять этот процесс на более низком уровне. Это позволяет нам лучше понимать, что происходит «за кулисами» при вызове метода.
Знание этой концепции полезно для написания более эффективного и надежного кода, особенно при решении более сложных задач ООП.
Дополнительные материалы
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.