En Python, ¿hay una manera de saber si un objeto “implementa una interfaz” antes de pasarlo a una función?

Sé que esto puede sonar como una pregunta estúpida, especialmente para alguien que conoce la naturaleza de Python, pero me preguntaba: ¿hay alguna forma de saber si un objeto “implementa una interfaz” para decir?

Para dar un ejemplo de lo que quiero decir:

digamos que tengo esta función:

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

Mi pregunta es: ¿hay una manera de asegurarse de que el objeto pasado a la función sea iterable ? Sé que en Java o C # puedo hacer esto haciendo que el método acepte cualquier objeto que implemente una interfaz específica, digamos (por ejemplo) iIterable como este: void get_counts(iIterable sequence)

Supongo que en Python tendría que emplear controles de introspección preventivos (¿en un decorator quizás?) Y lanzar una exception personalizada si el objeto no tiene un atributo __iter__ ). ¿Pero hay una forma más pythonica de hacer esto?

Utilice polymorphism y tipificación de pato antes de isinstance() o interfaces

Por lo general, define lo que quiere hacer con sus objetos, luego usa el polymorphism para ajustar la forma en que cada objeto responde a lo que quiere hacer, o usa la escritura de pato; prueba si el objeto en cuestión puede hacer lo que quieres hacer en primer lugar. Esta es la compensación entre invocación y introspección, la sabiduría convencional afirma que la invocación es preferible a la introspección, pero en Python, la tipificación de pato es preferible a la prueba de isinstance .

Por lo tanto, debe averiguar por qué necesita filtrar, ya sea o no, algo es iterable en primer lugar; ¿Por qué necesitas saber esto? Simplemente use un try: iter(object) , except TypeError: # not iterable puede probar.

O tal vez solo necesite lanzar una excepción si lo que se pasó no fue un error, ya que eso indicaría un error.

ABC

Con la escritura de pato, es posible que tenga que probar varios métodos y, por lo tanto, una prueba de isinstance() puede parecer una mejor opción. En tales casos, usar una Clase Base Abstracta (ABC) también podría ser una opción; usar un ABC le permite “pintar” varias clases diferentes como el tipo correcto para una operación determinada, por ejemplo. El uso de un ABC le permite centrarse en las tareas que deben realizarse en lugar de las implementaciones específicas utilizadas; puedes tener un ABC para Paintable , un ABC para Printable , etc.

Interfaces Zope y architecture de componentes.

Si encuentra que su aplicación está utilizando una gran cantidad de ABCs o si sigue teniendo que agregar métodos polimórficos a sus clases para enfrentar diferentes situaciones, el siguiente paso es considerar el uso de una architecture de componentes completa, como la Arquitectura de componentes Zope. (ZCA) .

zope.interface interfaces zope.interface son ABCs en esteroides, especialmente cuando se combinan con los adaptadores ZCA. Las interfaces documentan el comportamiento esperado de una clase:

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

Pero también te permite buscar adaptadores; en lugar de poner todos los comportamientos para cada uso de formas en sus clases, implementa adaptadores para proporcionar comportamientos polimórficos para casos de uso específicos. Puede adaptar sus objetos para que sean imprimibles, iterables o exportables a 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'{0}{0}'.format( frobnar.width, frobnar.height) return u''.join(entries) 

y su código simplemente tiene que buscar adaptadores para cada forma:

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

Python (desde 2.6) tiene clases base abstractas (también conocidas como interfaces virtuales), que son más flexibles que las interfaces Java o C #. Para verificar si un objeto es iterable, use collections.Iterable :

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

Sin embargo, si su bloque else solo generaría una excepción, entonces la respuesta más común de Python es: ¡no lo compruebe ! Depende de su interlocutor pasar el tipo apropiado; solo necesita documentar que está esperando un objeto iterable.

La forma Pythonic es usar la escritura de pato y “pedir perdón, no permiso”. Por lo general, esto significa realizar una operación en un bloque de try asumiendo que se comporta de la manera que usted espera, y luego manejar otros casos en un bloque de except .

Creo que esta es la forma en que la comunidad te recomendaría que lo hicieras:

 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) 

O, puedes usar algo como zope.interface .