Pregunta

Estoy usando la tipificación de pato en Python.

def flagItem(object_to_flag, account_flagging, flag_type, is_flagged):
    if flag_type == Flags.OFFENSIVE:
        object_to_flag.is_offensive=is_flagged
    elif flag_type == Flags.SPAM:
        object_to_flag.is_spam=is_flagged
    object_to_flag.is_active=(not is_flagged)
    object_to_flag.cleanup()
    return object_to_flag.put()

Cuando se pasan diferentes objetos en tan object_to_flag, todos los cuales tienen atributos is_active, is_spam, is_offensive. También sucede que tiene un método cleanup().

Los objetos que estoy pasando en todos tienen la misma clase de base (que son objetos db en Google App Engine):

class User(db.Model):
    ...
    is_active = db.BooleanProperty(default = True)
    is_spam = db.BooleanProperty(default=False)
    is_offensive = db.BooleanProperty(default=False)
    def cleanup():
        pass

class Post(db.Model):
    ...
    is_active = db.BooleanProperty(default = True)
    is_spam = db.BooleanProperty(default=False)
    is_offensive = db.BooleanProperty(default=False)
    def cleanup():
        pass

¿Cómo puedo hacer que el método cleanup() tan abstracto que pueda tener la misma clase padre para todos estos objetos que requieren los niños proporcionan la implementación?

Tal vez lo más importante, es este 'Pythonic'? Debería seguir este camino, o debo confiar en la tipificación de pato? Mi formación es en Java y estoy tratando de aprender la manera de hacer las cosas Python.

Gracias!

¿Fue útil?

Solución

Since you don't have abc available, you can do this with a simple metaclass

class Abstract(type(db.Model)):
    def __new__(metacls, clsname, bases, clsdict):
        for methodname in clsdict.pop('_abstract_methods_', []):
            try:
                if not callable(clsdict[methodname]):
                    raise TypeError("{0} must implement {1}".format(clcname, methodname))
            except KeyError:
                raise TypeError("{0} must implement {1}".format(clcname, methodname))
       return super(Abstract, metacls).__new__(metacls, clsname, bases, clsdict)


class RequireCleanup(db.Model):
    __metaclass__ = Abstract
    _abstract_methods_ = ['cleanup']

    def cleanup(self):
        pass

the expression type(db.Model) resolves to whatever metaclass gets used for db.Model so we don't step on google's code. Also, we pop _abstract_methods_ out of the class dictionary before it get's passed to google's __new__ method so that we don't break anything. If db.Model already has an attribute with that name, then you will need to change it to something else.

Otros consejos

Use the abc module. Specifically, set your base class's metaclass to ABCMeta and use the @abstractmethod decorator on your cleanup method.

The debate on whether this is "pythonic" is split. PEP 3119, which describes the standard, lists some of the pros and cons (but obviously favors ABCs). It made it into the standard library, which is a pretty good indication that many people consider it useful in some circumstances. In your case, I think it is appropriate.

If you want to ensure that the cleanup method is implemented, you can wrap with the @abc.virtualmethod decorator. This will cause an error on instantiation of any object that hasn't overridden the virtualmethod. This also requires that you make abc.ABCMeta your class's __metaclass__.

See the abc module for more info and some examples.

This is not commonly done: usually there will just be docs to the effect that implementers must override the given method. However this may be more due to the newness of the abc module (new in Python 2.6) than a perceived unpythonicness of this approach.

Why not have the cleanup method just raise an NotImplementedError when it is called? If they want your children classes to work they'll have to put some sort of implementation in place.

Although I've never used it personally, I've seen many references to Zope interfaces. This may be overkill for your task, but it may give you some direction. And it may feel comfortable to someone with a Java background.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top