2009-02-16 15 views
16

Así que estoy comenzando un proyecto usando Python después de pasar una cantidad significativa de tiempo en tierra estática. He visto algunos proyectos que hacen "interfaces" que en realidad son solo clases sin implementaciones. Antes, me burlaba de la idea e ignoraba esa sección de esos proyectos. Pero ahora, estoy empezando a calentarme a la idea."Interfaces" en Python: ¿sí o no?

Sólo para que quede claro, una interfaz en Python sería algo como esto:

class ISomething(object): 
    def some_method(): 
     pass 
    def some_other_method(some_argument): 
     pass 

Tenga en cuenta que usted no está de paso auto con cualquiera de los métodos, por lo que requiere que el método sea evitado para ser llamado. Veo esto como una buena forma de documentación y prueba de integridad.

¿Cuál es la opinión de todos aquí sobre la idea? ¿Me han lavado el cerebro por toda la programación de C# que he hecho, o es una buena idea?

+2

"exigir que se overriden el método que se llamará": some_other_method pero se puede llamar ISomething()() – Miles

+0

no parece muy Pythonic a mí.. –

Respuesta

10

Hay algunos casos en los que las interfaces pueden ser muy útiles. Twisted hace fairly extensive use de Zope interfaces, y en un proyecto que estaba trabajando en interfaces Zope funcionó muy bien. Los rasgos de Enthought empaquetados recientemente agregaron interfaces, pero no tengo ninguna experiencia con ellos.

Sin embargo, tenga cuidado con el uso excesivo: el tipado de pato y los protocolos son un aspecto fundamental de Python, solo use interfaces si son absolutamente necesarios.

5

Parece algo innecesario para mí: cuando estoy escribiendo clases como esa, normalmente hago la clase base (su ISomething) sin ningún método, y menciono en la documentación actual qué métodos se deben reemplazar las subclases.

3

estoy a punto de hacer algo similar con mi proyecto de Python, las únicas cosas que añadir son:

  • , cuerdas extra largos en profundidad doc para cada interfaz y todos los métodos abstractos.
  • Agregaría todos los argumentos requeridos para que haya una lista definitiva.
  • Plantee una excepción en lugar de 'pase'.
  • Prefijo todos los métodos por lo que son, obviamente, parte de la interfaz - Interfaz de Foo: def foo_method1()
+0

Solo para agregar: la excepción específica para subir sería NotImplementedError() – che

+1

Me gustan todas sus sugerencias, excepto la 4ta. Es innecesariamente detallado, IMO. Además, un enlace a los documentos NotImplementedError: http://docs.python.org/library/exceptions.html#exceptions.NotImplementedError –

+1

+1: Elevar NotImplementedError en lugar de pasar. -1: nunca use nombres adicionales como foo_method. Nunca. –

6

No creo que las interfaces agreguen nada al entorno del código.

  • Method definition enforcing sucede sin ellos. Si se espera que un objeto tenga como Foo y tenga el método bar(), y no lo hace, lanzará un AttributeError.
  • Simplemente asegurarse de que un método de interfaz se defina no garantiza su corrección; las pruebas de unidad de comportamiento deben estar en su lugar de todos modos.
  • Escribir la página "leer o morir" es tan efectivo como describir qué métodos debe tener su objeto para ser compatible con lo que está conectando con documentos elaborados en una clase de interfaz, ya que probablemente sea va a tener pruebas de todos modos. Una de esas pruebas puede ser estándar para todos los objetos compatibles que verificarán el tipo de invocación y devolución de cada método base.
10

La manera pitónica es "Pedir perdón en lugar de recibir permiso".Las interfaces son todas para recibir permiso para realizar alguna operación en un objeto. Python prefiere esto:

def quacker(duck): 
    try: 
     duck.quack(): 
    except AttributeError: 
     raise ThisAintADuckException 
4

Puede crear una interfaz en un lenguaje de tipos dinámicos, pero no hay aplicación de la interfaz en tiempo de compilación. Un compilador de lenguaje tipado estáticamente le advertirá si olvida implementar (o ¡mal!) Un método de una interfaz. Como no recibe dicha ayuda en un lenguaje de tipado dinámico, su declaración de interfaz solo sirve como documentación. (Lo cual no es necesariamente malo, es solo que su declaración de interfaz no proporciona ninguna ventaja en tiempo de ejecución versus escribir comentarios.)

27

No estoy seguro de qué es eso. Las interfaces (de esta forma, de todos modos) son en gran parte para evitar la herencia múltiple. Pero Python tiene MI, ¿por qué no hacer una clase abstracta?

class Something(object): 
    def some_method(self): 
     raise NotImplementedError() 
    def some_other_method(self, some_argument): 
     raise NotImplementedError() 
+0

+1: Iba a sugerir lo mismo, pero me ganaste. Esto es mucho más Pythonic porque da el error apropiado cuando el método no se implementa, pero no requiere que se implemente un método si no se usará en el contexto de la subclase. –

+0

Además, al escribir Java uno a menudo escribe interfaces más clases abstractas. La subclase extiende una clase abstracta, que implementa la interfaz. La interfaz es redundante, pero se espera debido a la convención. Duck typing elimina la idiotez, pero a veces encuentro este tipo de clase base muy útil. –

11

En Python 2.6 y versiones posteriores, puede utilizar abstract base classes lugar. Estos son útiles, porque luego puede probar para ver si algo implementa un ABC dado usando "isinstance". Como es habitual en Python, el concepto no se aplica tan estrictamente como sería en un lenguaje estricto, pero es útil. Además, hay formas idiomáticas agradables de declarar métodos abstractos con decoradores - vea el enlace de arriba para ejemplos.

1

Yo personalmente uso interfaces mucho en conjunto con Zope Component Architecture (ZCA). La ventaja no es tanto tener interfaces sino poder usarlas con adaptadores y utilidades (singletons).

E.g. podrías crear un adaptador que pueda tomar una clase que implemente ISomething pero la adapte a la interfaz ISomethingElse. Básicamente es un envoltorio.

la clase original sería:

class MyClass(object): 
    implements(ISomething) 

    def do_something(self): 
     return "foo" 

entonces se puede imaginar interfaz ISomethingElse tiene una do_something_else método(). Un adaptador podría tener este aspecto:

class SomethingElseAdapter(object): 
    implements(ISomethingElse) 
    adapts(ISomething) 

    def __init__(self, context): 
     self.context = context 

    def do_something_else(): 
     return self.context.do_something()+"bar" 

A continuación registraría que el adaptador con el registro de componentes y, a continuación, puede utilizar de esta manera:

>>> obj = MyClass() 
>>> print obj.do_something() 
"foo" 
>>> adapter = ISomethingElse(obj) 
>>> print adapter.do_something_else() 
"foobar" 

Lo que le da es la capacidad de extender la clase original con funcionalidad que la clase no proporciona directamente. Puede hacerlo sin cambiar esa clase (puede estar en un producto/biblioteca diferente) y simplemente puede intercambiar ese adaptador por una implementación diferente sin cambiar el código que lo utiliza. Todo se hace mediante el registro de componentes en tiempo de inicialización.

Esto, por supuesto, es principalmente útil para marcos/bibliotecas.

Creo que toma tiempo acostumbrarse, pero realmente ya no quiero vivir sin él. Pero como se dijo antes, también es cierto que debe pensar exactamente dónde tiene sentido y dónde no. Por supuesto, las interfaces por sí mismas también pueden ser útiles como documentación de la API. También es útil para pruebas unitarias donde puedes probar si tu clase realmente implementa esa interfaz. Y por último pero no menos importante, me gusta comenzar escribiendo la interfaz y algunos doctests para tener la idea de lo que realmente voy a codificar.

Para obtener más información, puede consultar mi little introduction to it y hay un quite extensive description of it's API.

1

¿Ha mirado PyProtocols? tiene una implementación de interfaz agradable que debe tener en cuenta.

Cuestiones relacionadas