Есть ли способ получить специальный полиморфизм в Python?

Многие языки поддерживают специальный полиморфизм (перегрузка функций) из коробки. Однако, похоже, что Python отказался от этого. Тем не менее, я могу предположить, что может быть какая-то хитрость или библиотека, способная осуществить это в Python. Кто-нибудь знает такой инструмент?

Например, в Haskell можно использовать это для генерации тестовых данных для разных типов:

-- In some testing library:
class Randomizable a where
   genRandom :: a

-- Overload for different types
instance Randomizable String where genRandom = ...
instance Randomizable Int    where genRandom = ...
instance Randomizable Bool   where genRandom = ...


-- In some client project, we might have a custom type:
instance Randomizable VeryCustomType where genRandom = ...

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

Как бы вы достигли чего-то подобного в Python?

3 ответа

Решение

Python не является строго типизированным языком, поэтому действительно не имеет значения, есть ли у вас экземпляр Randomizable или экземпляр какого-то другого класса, который имеет те же методы.

Один из способов получить то, что вы хотите, может быть таким:

types_ = {}
def registerType ( dtype , cls ) :
    types_[dtype] = cls
def RandomizableT ( dtype ) :
    return types_[dtype]

Во-первых, да, я определил функцию с заглавной буквой, но она должна действовать как класс. Например:

registerType ( int , TheLibrary.Randomizable )
registerType ( str , MyLibrary.MyStringRandomizable )

Тогда позже:

type = ... # get whatever type you want to randomize
randomizer = RandomizableT(type) ()
print randomizer.getRandom()

Функция Python не может быть автоматически специализирована на основе статической типизации во время компиляции. Поэтому его результат может зависеть только от его аргументов, полученных во время выполнения и от глобальной (или локальной) среды, если только сама функция не может быть модифицирована на месте и может нести некоторое состояние.

Ваша общая функция genRandom не принимает аргументов, кроме информации о наборе. Таким образом, в Python он должен по крайней мере получить тип в качестве аргумента. Поскольку встроенные классы не могут быть изменены, реализация обобщенной функции (экземпляра) для таких классов должна каким-либо образом предоставляться через глобальную среду или включаться в саму функцию.

Я обнаружил, что начиная с Python 3.4, есть @functools.singledispatch декоратор. Однако он работает только для функций, которые получают экземпляр типа (объект) в качестве первого аргумента, поэтому неясно, как его можно применить в вашем примере. Я также немного смущен его обоснованием:

Кроме того, в настоящее время для кода Python является обычным антипаттерном проверять типы полученных аргументов, чтобы решить, что делать с объектами.

Я понимаю, что анти-паттерн - это жаргонный термин для паттерна, который считается нежелательным (и вовсе не означает отсутствие паттерна). Таким образом, обоснование утверждает, что проверка типов аргументов нежелательна, и эта претензия используется для обоснования введения инструмента, который упростит... диспетчеризацию типа аргумента. (Между прочим, обратите внимание, что согласно PEP 20 "явное лучше, чем неявное".)

Раздел "Альтернативные подходы" в PEP 443 "Универсальные функции с одной отправкой", тем не менее, заслуживает прочтения. Есть несколько ссылок на возможные решения, в том числе один на статью "Пятиминутные мультиметоды в Python" Гвидо ван Россума от 2005 года.

Имеет ли это значение полиморфизм рекламы?

class A:
    def __init__(self):
        pass

    def aFunc(self):
        print "In A"

class B:
    def __init__(self):
        pass

    def aFunc(self):
        print "In B"

f = A()
f.aFunc()
f = B()
f.aFunc()

выход

In A
In B

Еще одна версия полиморфизма

from module import aName

Если два модуля используют один и тот же интерфейс, вы можете импортировать любой из них и использовать его в своем коде. Одним из примеров этого является from xml.etree.ElementTree import XMLParser

Другие вопросы по тегам