В python есть способ узнать, реализует ли объект интерфейс, прежде чем передать его функции?

Я знаю, что это может звучать как глупый вопрос, особенно для тех, кто знает природу Python, но мне просто интересно, есть ли способ узнать, реализует ли объект "интерфейс", чтобы сказать?

Чтобы привести пример того, что я хочу сказать:

допустим, у меня есть эта функция:

def get_counts(sequence):
     counts = {}
     for x in sequence:
         if x in counts:
             counts[x] += 1
         else:
             counts[x] = 1
     return counts

Мой вопрос: есть ли способ убедиться, что объект, переданный в функцию iterable ? Я знаю, что в Java или C# я мог бы сделать это, если бы метод принимал любой объект, который реализует определенный интерфейс, скажем (например) iIterable как это: void get_counts(iIterable sequence)

Я предполагаю, что в Python мне пришлось бы использовать упреждающие проверки самоанализаdecorator возможно?) и кинуть кастом exception если объект не имеет __iter__ атрибуты). Но есть ли более питонный способ сделать это?

4 ответа

Решение

Используйте полиморфизм и типизацию утки перед isinstance() или интерфейсы

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

Таким образом, вам нужно выяснить, почему вам нужно фильтровать по мокрому или нет что-то итеративное в первую очередь; Зачем тебе это нужно знать? Просто используйте try: iter(object), except TypeError: # not iterable тестировать.

Или, возможно, вам просто нужно вызвать исключение, если все, что было передано, не было итеративным, так как это означало бы ошибку.

ABCs

При вводе утки вы можете обнаружить, что вам нужно тестировать несколько методов, и, таким образом, isinstance() Тест может выглядеть лучше. В таких случаях использование абстрактного базового класса (ABC) также может быть вариантом; например, используя ABC, вы "рисуете" несколько разных классов как правильный тип для данной операции. Используя ABC, мы сосредоточимся на задачах, которые необходимо выполнить, а не на конкретных используемых реализациях; Вы можете иметь Paintable ABC, а Printable Азбука и др.

Zope интерфейсы и компонентная архитектура

Если вы обнаружите, что ваше приложение использует очень много ABC или вам по-прежнему приходится добавлять полиморфные методы в ваши классы для работы в различных ситуациях, то следующим шагом будет рассмотреть возможность использования полнофункциональной компонентной архитектуры, такой как Zope Component Architecture (ZCA).

zope.interface интерфейсы - это ABC на стероидах, особенно в сочетании с адаптерами ZCA. Интерфейсы документируют ожидаемое поведение класса:

if IFrobnarIterable.providedBy(yourobject):
    # it'll support iteration and yield Frobnars.

но это также позволяет вам искать адаптеры; вместо того, чтобы помещать все поведения для каждого использования фигур в ваши классы, вы реализуете адаптеры, чтобы обеспечить полиморфное поведение для конкретных вариантов использования. Вы можете адаптировать свои объекты для печати, итерации или экспорта в XML:

class FrobnarsXMLExport(object):
    adapts(IFrobnarIterable)
    provides(IXMLExport)

    def __init__(self, frobnariterator):
        self.frobnars = frobnariterator

    def export(self):
        entries = []
        for frobnar in self.frobnars:
            entries.append(
                u'<frobnar><width>{0}</width><height>{0}</height></frobnar>'.format(
                    frobnar.width, frobnar.height)
        return u''.join(entries)

и ваш код просто должен искать адаптеры для каждой фигуры:

for obj in setofobjects:
    self.result.append(IXMLExport(obj).export())

Python (начиная с 2.6) имеет абстрактные базовые классы (или виртуальные интерфейсы), которые более гибки, чем интерфейсы Java или C#. Чтобы проверить, является ли объект итеративным, используйте collections.Iterable:

if isinstance(obj, collections.Iterable):
    ...

Однако, если ваш else Блок просто вызовет исключение, тогда самый ответ Python: не проверять! Ваш абонент должен передать соответствующий тип; вам просто нужно документально подтвердить, что вы ожидаете итеративный объект.

Pythonic способ состоит в том, чтобы использовать печатание на клавиатуре утки и, "просите прощение, не разрешение". Обычно это означает выполнение операции в try блок, если он ведет себя так, как вы ожидаете, а затем обрабатывает другие случаи в except блок.

Я думаю, что это способ, которым сообщество рекомендовало бы вам сделать это:

import sys

def do_something(foo):
    try:
        for i in foo:
            process(i)
    except:
        t, ex, tb = sys.exc_info()
        if "is not iterable" in ex.message:
            print "Is not iterable"

do_something(True)

Или вы можете использовать что-то вроде zope.interface,

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