Decoradores de clase en Python: casos de uso prácticos

9

Estoy buscando casos de uso prácticos y no sintéticos de decoradores de clase Python. Hasta ahora, el único caso que tenía sentido para mí es registrar una clase en un sistema editor-suscriptor, por ejemplo, complementos o eventos, algo como:

@register
class MyPlugin(Plugin):
    pass

o

@recieves_notifications
class Console:
    def print(self, text):
        ...

Cualquier otro caso sensato en el que haya estado pensando podría haberse construido sobre la herencia, las metaclases o los métodos de decoración. ¿Podrías compartir ejemplos buenos (o malos) del uso de decoradores de clase?

¡Gracias!

Zaur Nasibov
fuente
Para empezar, los decoradores son conceptualmente mucho más simples que las metaclases. Es mucho más simple escribir un decorador que una metaclase, y evita posiblemente tener que descubrir cómo combinar múltiples metaclases sin romper nada.
amon
@amon cuando un desarrollador experimentado usa una metaclase, por lo general significa que otras opciones se han considerado a fondo y que la metaclase es la mejor manera de resolver el problema. Además, explícito es mejor que implícito ; esa puede ser la razón por la que (como ejemplo) tenemos ABCMeta, no el @abstractclassdecorador de clases.
Zaur Nasibov
2
Soy un programador de Python, y honestamente no veo la necesidad de ellos. El hecho de que exista una función de idioma no significa que deba usarla o que incluso deba usarla. Los diseñadores presentan todo tipo de características locas que resultan innecesarias o incluso perjudiciales en retrospectiva. De hecho, mencionó otras dos características similares en su pregunta :)
gardenhead
1
Sin algo que no podría vivir sin él, pero algo que he encontrado útil es usar un decorador de clase para registrar todas las entradas y salidas de todos los métodos de la clase.
bgusach

Respuestas:

8

Al escribir pruebas unitarias que tengan el @unittest.skipIfy @unittest.skipUnlessse pueden usar en métodos individuales o en una unittest.TestCasedefinición de clase completa . Esto hace que escribir pruebas para problemas específicos de la plataforma sea mucho más fácil.

Ejemplo de asyncio/test_selectors

# Some platforms don't define the select.kqueue object for these tests.
# On those platforms, this entire grouping of tests will be skipped.

@unittest.skipUnless(hasattr(selectors, 'KqueueSelector'),
                 "Test needs selectors.KqueueSelector)")
class KqueueSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn):
...
Sethmlarson
fuente
2

Los decoradores de clase se crearon explícitamente para hacer que algunas cosas que ya se podían expresar mediante metaclases fueran más agradables, desde el PEP :

El caso de uso motivador fue hacer que ciertas construcciones se expresen más fácilmente y que dependan menos de los detalles de implementación del intérprete de CPython. Si bien es posible expresar una funcionalidad similar a un decorador de clase utilizando metaclases, los resultados son generalmente desagradables y la implementación altamente frágil. Además, las metaclases se heredan, mientras que los decoradores de clases no lo son, lo que hace que las metaclases no sean adecuadas para algunos usos específicos de clase de los decoradores de clases. El hecho de que los proyectos de Python a gran escala como Zope estuvieran pasando por estas contorsiones salvajes para lograr algo así como los decoradores de clase se ganó el BDFL.

quodlibetor
fuente
Gracias por citar PEP-3129. Sin embargo, la pregunta no es por qué existen los decoradores de clase, qué piensa Guido de ellos y cómo Zope o IronPython podrían haberlos usado antes de que surgieran. La pregunta es sobre su uso práctico hoy, en el año 2016.
Zaur Nasibov
1
Correcto, el punto es que la pregunta original presenta las metaclases como algo que se puede usar para implementar una funcionalidad similar a un decorador de clases, pero el objetivo de los decoradores es que son más fáciles de usar y más obvias que las metaclases. Entonces, la respuesta a esa parte de la pregunta es "Si está dividido entre decoradores o metaclases, debe usar decoradores porque son más fáciles de entender para los demás".
quodlibetor
Estás dejando más preguntas que respuestas. "... el objetivo de los decoradores es que son más fáciles de usar y más obvios que las metaclases" - ¿En qué casos? Entonces "... deberías usar decoradores porque son más fáciles de entender para los demás" ¿En serio? Durante mucho tiempo de adicción a Python, he visto toneladas de metaclases, algunas feas y otras hermosas. Pero no he descubierto ningún decorador de clase en absoluto.
Zaur Nasibov
1

De esta pregunta de desbordamiento de pila:

def singleton(class_):
  instances = {}
  def getinstance(*args, **kwargs):
    if class_ not in instances:
        instances[class_] = class_(*args, **kwargs)
    return instances[class_]
  return getinstance

@singleton
class MyClass(BaseClass):
  pass
Jared Smith
fuente
Sí, pero la contra se da en la misma pregunta SO: la metaclase es un mejor enfoque.
Zaur Nasibov
1

Buscar casos de uso que se puedan hacer con decoradores en clases es inútil; todo lo que se puede hacer con decoradores en clases se puede hacer con metaclases. Incluso su ejemplo de registro. Para probarlo, aquí hay una metaclase que aplica un decorador:

Python 2:

def register(target):
    print 'Registring', target
    return target


class ApplyDecorator(type):
    def __new__(mcs, name, bases, attrs):
        decorator = attrs.pop('_decorator')
        cls = type(name, bases, attrs)
        return decorator(cls)

    def __init__(cls, name, bases, attrs, decorator=None):
        super().__init__(name, bases, attrs)


class Foo:
    __metaclass__ = ApplyDecorator
    _decorator = register

Python 3:

def register(target):
    print('Registring', target)
    return target


class ApplyDecorator(type):
    def __new__(mcs, name, bases, attrs, decorator):
        cls = type(name, bases, attrs)
        return decorator(cls)

    def __init__(cls, name, bases, attrs):
        super().__init__(name, bases, attrs)


class Foo(metaclass=ApplyDecorator, decorator=register):
    pass
Idan Arye
fuente
1
Como dices, cualquier cosa se puede hacer con decorador. La pregunta es, ¿cuáles son los casos que deben hacerse a través de decoradores de clase en lugar de metaclases?
Zaur Nasibov