Какова наилучшая поддерживаемая реализация универсальных функций для Python? - PullRequest
8 голосов
/ 18 сентября 2009

A универсальная функция отправляется на основе типа всех ее аргументов. Программист определяет несколько реализаций функции. Правильный выбор выбирается во время вызова на основе типов его аргументов. Это полезно для адаптации объекта среди других вещей. Python имеет несколько общих функций, включая len().

Эти пакеты допускают код, который выглядит следующим образом:

@when(int)
def dumbexample(a):
    return a * 2

@when(list)
def dumbexample(a):
    return [("%s" % i) for i in a]

dumbexample(1) # calls first implementation
dumbexample([1,2,3]) # calls second implementation

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

class WebComponentUserAdapter(object):
    def __init__(self, guest):
        self.guest = guest
    def canDoSomething(self):
        return guest.member_of("something_group")

@when(my.webframework.User)
componentNeedsAUser(user):
    return WebComponentUserAdapter(user)

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

Я знаком с Зопой zope.component.queryAdapter(object, ISomething). Программист регистрирует вызываемый адаптер, который принимает определенный класс объекта в качестве аргумента и возвращает что-то совместимое с интерфейсом. Это полезный способ разрешить плагины. В отличие от исправлений обезьяны, он работает, даже если объект должен адаптироваться к нескольким интерфейсам с одинаковыми именами методов.

Ответы [ 3 ]

7 голосов
/ 18 сентября 2009

Я бы порекомендовал библиотеку PEAK-Rules от P. Eby. Тем же автором (хотя и не рекомендуется) является пакет RuleDispatch (предшественник PEAK-Rules). Последний больше не поддерживается IIRC.

PEAK-Rules имеет много приятных особенностей, одна из которых заключается в том, что он (ну, не легко, но) расширяемый. Помимо «классической» отправки по типам «ony», в ней также предусмотрена отправка произвольных выражений в качестве «хранителей».

Функция len() не является истинной обобщенной функцией (по крайней мере, в смысле упомянутых выше пакетов, а также в том смысле, что этот термин используется в таких языках, как Common Lisp , Dylan или Cecil ), поскольку это просто удобный синтаксис для вызова специально названного (но в остальном регулярного) метода:

len(s) == s.__len__()

Также обратите внимание, что это только одна отправка, то есть фактический получатель (s в приведенном выше коде) определяет вызванную реализацию метода. И даже гипотетический

def call_special(receiver, *args, **keys):
    return receiver.__call_special__(*args, **keys)

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

Это отличается от множественной диспетчеризации, где нет выделенного получателя, и все аргументы используются для того, чтобы найти фактическую реализацию метода для вызова. Это то, что на самом деле делает все это стоящим. Если бы это был только какой-то странный синтаксический сахар, никто бы не стал его использовать, ИМХО.

from peak.rules import abstract, when

@abstract
def serialize_object(object, target):
    pass

@when(serialize_object, (MyStuff, BinaryStream))
def serialize_object(object, target):
    target.writeUInt32(object.identifier)
    target.writeString(object.payload)

@when(serialize_object, (MyStuff, XMLStream))
def serialize_object(object, target):
    target.openElement("my-stuff")
    target.writeAttribute("id", str(object.identifier))
    target.writeText(object.payload)
    target.closeElement()

В этом примере вызов похож на

serialize_object(MyStuff(10, "hello world"), XMLStream())

рассматривает оба аргумента, чтобы решить, какой метод на самом деле должен быть вызван.

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

0 голосов
/ 18 сентября 2009

Я не вижу смысла в этих "общих" функциях. Звучит как простой полиморфизм.

Ваши «общие» функции могут быть реализованы таким образом, не прибегая к какой-либо идентификации типа во время выполнения.

class intWithDumbExample( int ):
    def dumbexample( self ):
        return self*2

class listWithDumbExmaple( list ):
    def dumpexample( self ):
        return [("%s" % i) for i in self]

def dumbexample( a ):
    return a.dumbexample()
0 голосов
/ 18 сентября 2009

Вы можете использовать такую ​​конструкцию:

def my_func(*args, **kwargs):
    pass

В этом случае args будет списком любых безымянных аргументов, а kwargs будет словарем именованных. Отсюда вы можете обнаружить их типы и действовать соответствующим образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...