Я знаю, что Python не поддерживает перегрузку методов, но я столкнулся с проблемой, которую не могу решить хорошим способом Pythonic.
Я делаю игру, в которой персонаж должен стрелять различными пулями, но как мне написать разные функции для создания этих пуль? Например, предположим, у меня есть функция, которая создает пулю, путешествующую из точки А в точку Б с заданной скоростью. Я бы написал такую функцию:
def add_bullet(sprite, start, headto, speed):
... Code ...
Но я хочу написать другие функции для создания пуль, такие как:
def add_bullet(sprite, start, direction, speed):
def add_bullet(sprite, start, headto, spead, acceleration):
def add_bullet(sprite, script): # For bullets that are controlled by a script
def add_bullet(sprite, curve, speed): # for bullets with curved paths
... And so on ...
И так со многими вариациями. Есть ли лучший способ сделать это, не используя так много ключевых аргументов, потому что это становится довольно быстро. Переименование каждой функции очень плохо тоже , потому что вы получаете либо add_bullet1
, add_bullet2
или add_bullet_with_really_long_name
.
Чтобы ответить на некоторые ответы:
Нет, я не могу создать иерархию класса Bullet, потому что это слишком медленно. Фактический код для управления маркерами находится на C, а мои функции - обертки вокруг C API.
Я знаю об аргументах ключевых слов, но проверка на все виды комбинаций параметров становится раздражающей, но аргументы по умолчанию помогают выделить как
acceleration=0
default value + if + else
чтобы сделать то же самое, что и C ++. Это одна из немногих вещей, которые C ++ имеет лучшую читаемость, чем Python ...script, curve
есть ли у них общий предок, какие методы они поддерживают. С помощью duck-typing вам нужно, чтобы дизайн класса выяснил, какие методы они должны поддерживать. ПредположительноScript
поддерживает своего рода обратный вызов на основе временного шага (но какой объект должен возвращать «позицию на этом временном шаге» траекторию на этом временном шаге »). Предположительно,start, direction, speed
иstart, headto, spead, acceleration
оба описывают типы траекторий, но опять же, вы должны разработать принимающий класс, чтобы знать, как распаковать его и обработать.Ответы:
То, что вы просите, называется многократной отправкой . Смотрите примеры языка Julia, которые демонстрируют различные типы рассылок.
Однако, прежде чем смотреть на это, мы сначала рассмотрим, почему перегрузка не совсем то, что вы хотите в python.
Почему бы не перегружать?
Во-первых, нужно понять концепцию перегрузки и почему она не применима к питону.
Python является динамически типизированным языком, поэтому концепция перегрузки просто к нему не относится. Однако еще не все потеряно, так как мы можем создавать такие альтернативные функции во время выполнения:
Таким образом, мы должны быть в состоянии сделать мультиметоды в Python - или, как это называется альтернативно: множественная диспетчеризация .
Многократная отправка
Мультиметоды также называют многократной отправкой :
Python не поддерживает это из коробки 1 , но, как это бывает, есть отличный пакет python, называемый multipledispatch, который делает именно это.
Решение
Вот как мы можем использовать пакет multipledispatch 2 для реализации ваших методов:
1. Python 3 в настоящее время поддерживает однократную отправку
2. Старайтесь не использовать multipledispatch в многопоточной среде, иначе вы получите странное поведение.
источник
speed
могло бы измениться в середине функции, когда другой поток устанавливает свое собственное значениеspeed
) !!! Мне потребовалось много времени, чтобы понять, что именно библиотека была виновником.Python поддерживает «перегрузку методов» в том виде, в каком вы ее представляете. На самом деле, то, что вы только что описали, тривиально реализовать в Python разными способами, но я бы сказал:
В приведенном выше коде
default
допустимое значение по умолчанию для этих аргументов, илиNone
. Затем вы можете вызвать метод только с интересующими вас аргументами, и Python будет использовать значения по умолчанию.Вы также можете сделать что-то вроде этого:
Другая альтернатива - напрямую подключить нужную функцию к классу или экземпляру:
Еще один способ - использовать абстрактный шаблон фабрики:
источник
if sprite and script and not start and not direction and not speed...
просто знать, что это в конкретном действии. потому что вызывающий абонент может вызвать функцию, предоставив все доступные параметры. Во время перегрузки определите для вас точные наборы соответствующих параметров.Для перегрузки функций вы можете использовать решение «по своему усмотрению». Этот скопирован из статьи Гвидо ван Россума о мультиметодах (потому что разница между mm и перегрузкой в python невелика):
Использование будет
Наиболее ограничительные ограничения на данный момент :
источник
Возможный вариант - использовать модуль multipledispatch, как описано здесь: http://matthewrocklin.com/blog/work/2014/02/25/Multiple-Dispatch
Вместо этого:
Ты можешь сделать это:
В результате использования:
источник
В Python 3.4 был добавлен PEP-0443. Универсальные функции единой отправки .
Вот краткое описание API от PEP.
Чтобы определить универсальную функцию, украсьте ее с помощью декоратора @singledispatch. Обратите внимание, что отправка происходит по типу первого аргумента. Создайте свою функцию соответственно:
Чтобы добавить перегруженные реализации в функцию, используйте атрибут register () обобщенной функции. Это декоратор, принимающий параметр типа и декорирующий функцию, реализующую операцию для этого типа:
источник
Этот тип поведения обычно решается (на языках ООП) с использованием полиморфизма. Каждый тип пули будет отвечать за знание того, как она движется. Например:
Передайте как можно больше аргументов в c_function, а затем определите, какую функцию c вызывать, основываясь на значениях в исходной функции c. Таким образом, python должен вызывать только одну функцию c. Эта одна функция c смотрит на аргументы, а затем может соответствующим образом делегировать другим функциям c.
По сути, вы просто используете каждый подкласс в качестве отдельного контейнера данных, но, определяя все потенциальные аргументы базового класса, подклассы могут игнорировать те, с которыми они ничего не делают.
Когда появляется новый тип маркера, вы можете просто определить еще одно свойство на базе, изменить одну функцию python, чтобы она передавала дополнительное свойство, и одну функцию c_function, которая проверяет аргументы и делегирует соответственно. Кажется, звучит не так уж плохо.
источник
pos+v*t
а затем сравнения с границами экранаif x > 800
и так далее. Вызов этих функций несколько сотен раз за кадр оказался недопустимо медленным. Это было что-то вроде 40 кадров в секунду на 100% процессора с чистым питоном до 60 кадров в секунду с 5% -10%, когда сделано в C.add_bullet
и извлеките все поля, которые вам нужны. Я отредактирую свой ответ.При прохождении арг ключевых слов .
источник
Либо используйте несколько аргументов ключевого слова в определении, либо создайте
Bullet
иерархию, экземпляры которой передаются в функцию.источник
Я думаю, что ваше основное требование - иметь синтаксис, подобный C / C ++, в python с минимально возможной головной болью. Хотя мне понравился ответ Александра Полуэктова, он не работает на уроках.
Следующее должно работать для классов. Он работает, различая количество аргументов без ключевых слов (но не поддерживает различение по типу):
И это можно использовать просто так:
Вывод:
источник
@overload
Декоратора был добавлен с оттенками типа (PEP 484). Хотя это не меняет поведение python, оно облегчает понимание происходящего и позволяет mypy обнаруживать ошибки.Смотрите: Тип подсказки и PEP 484
источник
Я думаю, что
Bullet
иерархия классов с ассоциированным полиморфизмом - это путь. Вы можете эффективно перегрузить конструктор базового класса, используя метакласс, так что вызов базового класса приводит к созданию соответствующего объекта подкласса. Ниже приведен пример кода для иллюстрации сути того, что я имею в виду.обновленный
Код был изменен для запуска под Python 2 и 3, чтобы сохранить его актуальность. Это было сделано таким образом, чтобы избежать использования явного синтаксиса метакласса Python, который варьируется между двумя версиями.
Для достижения этой цели
BulletMetaBase
экземплярBulletMeta
класса создается путем явного вызова метакласса при созданииBullet
базового класса (вместо использования__metaclass__=
атрибута класса илиmetaclass
аргумента ключевого слова в зависимости от версии Python).Вывод:
источник
Bullet
подклассов без необходимости изменять базовый класс или функцию фабрики каждый раз, когда вы добавляете другой подтип. (Конечно, если вы используете C, а не C ++, я думаю, у вас нет классов.) Вы также можете создать более умный метакласс, который сам определит, какой подкласс создать, основываясь на типе и / или числе. переданных аргументов (как в C ++ для поддержки перегрузки).Python 3.8 добавил functools.singledispatchmethod
Вывод
Вывод:
источник
Используйте ключевые аргументы со значениями по умолчанию. Например
В случае прямой пули по сравнению с изогнутой, я бы добавил две функции:
add_bullet_straight
иadd_bullet_curved
.источник
перегрузка методов сложна в питоне. Однако может использоваться передача переменных dict, list или primitive.
Я попробовал кое-что для своих вариантов использования, это могло бы помочь здесь понять людей, чтобы перегружать методы.
Давайте возьмем ваш пример:
метод перегрузки класса с вызовом методов из другого класса.
передать аргументы из удаленного класса:
ИЛИ
Таким образом, достигается обработка для списка, словаря или примитивных переменных из-за перегрузки метода.
попробуйте это для ваших кодов.
источник
Просто простой декоратор
Вы можете использовать это так
Измените его, чтобы адаптировать его к вашему варианту использования.
self/this
аргумента. Тем не менее, большинство языков делают это только дляthis
аргумента. Вышеуказанный декоратор распространяет идею на несколько параметров.Чтобы разобраться, предположим статический язык и определить функции
При статической диспетчеризации (перегрузке) вы увидите «номер, вызываемый» дважды, потому что
x
он был объявлен какNumber
, и это все , что нужно для перегрузки. С динамической диспетчеризацией вы увидите «целое число вызвано, поплавок вызвано», потому что это фактические типыx
в момент вызова функции.источник
x
для динамической диспетчеризации, и в каком порядке оба метода были вызваны для статической диспетчеризации. Рекомендую вам отредактировать печатные заявления иprint('number called for Integer')
т. Д.