En Python, he visto la recomendación de usar la celebración o el ajuste para extender la funcionalidad de un objeto o clase, en lugar de la herencia. En particular, creo que Alex Martelli habló sobre esto en su charla Python Design Patterns. He visto este patrón usado en bibliotecas para inyección de dependencia, como pycontainer.Cómo envolver objetos para ampliar/agregar funcionalidad mientras se trabaja alrededor de isinstance
Un problema que me he encontrado es que cuando tengo que interactuar con el código que utiliza el isinstance anti-pattern, este patrón falla porque el objeto de retención/envoltura no pasa la prueba isinstance
. ¿Cómo puedo configurar el objeto de sujeción/envoltura para evitar la verificación innecesaria de tipos? ¿Se puede hacer esto genéricamente? En cierto sentido, necesito algo para las instancias de clase análogas a los decoradores de funciones que preservan la firma (por ejemplo, simple_decorator o decorator de Michele Simionato).
Calificación: No estoy afirmando que todo el uso de isinstance
sea inapropiado; varias respuestas dan buenos puntos sobre esto. Dicho esto, debe reconocerse que el uso de isinstance
presenta limitaciones significativas en las interacciones de objetos --- Obliga a herencia a ser la fuente de polimorfismo, en lugar de comportamiento.
Parece haber cierta confusión acerca de cómo/por qué esto es un problema, así que permítanme darles un ejemplo simple (ampliamente levantado de pycontainer). Digamos que tenemos una clase Foo, así como una FooFactory. Por el bien del ejemplo, supongamos que queremos ser capaces de instanciar objetos Foo que registran cada llamada a la función, o no ... piense en AOP. Además, queremos hacer esto sin modificar la clase/fuente Foo de ninguna manera (por ejemplo, podemos estar implementando una fábrica genérica que pueda agregar capacidad de registro a cualquier instancia de clase sobre la marcha). Un primer intento de esto podría ser:
class Foo(object):
def bar():
print 'We\'re out of Red Leicester.'
class LogWrapped(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __getattr__(self, name):
attr = getattr(self.wrapped, name)
if not callable(attr):
return attr
else:
def fun(*args, **kwargs):
print 'Calling ', name
attr(*args, **kwargs)
print 'Called ', name
return fun
class FooFactory(object):
def get_foo(with_logging = False):
if not with_logging:
return Foo()
else:
return LogWrapped(Foo())
foo_fact = FooFactory()
my_foo = foo_fact.get_foo(True)
isinstance(my_foo, Foo) # False!
Hay razones pueden por las que podría querer hacer las cosas exactamente de esta manera (uso decoradores lugar, etc.) pero tenga en cuenta:
- Nos Don No quiero tocar la clase Foo. Supongamos que estamos escribiendo un código de marco que podría ser utilizado por clientes que aún no conocemos.
- El punto es devolver un objeto que es esencialmente un Foo, pero con funcionalidad añadida. Debería parecer un Foo --- tanto como sea posible --- a cualquier otro código de cliente esperando un Foo. De ahí el deseo de trabajar alrededor de
isinstance
. - Sí, sé que no necesito la clase de fábrica (defendiéndome preemptivamente aquí).
Agita mi puño en la furia impotente en isinstance. ¿No puedes arreglar el código con isinstance? http://stackoverflow.com/questions/423823/whats-your-favorite-programmer-ignorance-pet-peeve/423857#423857 –
Furia desamparado :) En algunos casos, tengo acceso a isinstance. Pero otro caso de uso sería querer escribir el código "framework" (por ejemplo, pycontainer arriba) que funciona bien con otros. – zweiterlinde