Cómo requerir la aplicación de método en Python?
-
04-10-2019 - |
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!
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.