1

我在 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()

不同对象作为 传入的地方object_to_flag,都具有is_active, is_spam,is_offensive属性。他们也碰巧有一个cleanup()方法。

我传入的对象都具有相同的基类(它们是 Google App Engine 中的 db 对象):

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

我怎样才能使cleanup()方法抽象,以便我可以为所有这些需要子提供实现的对象拥有相同的父类?

也许更重要的是,这是“pythonic”吗?我应该走这条路,还是应该只依靠鸭子打字?我的背景是 Java,我正在尝试学习 Python 的做事方式。

谢谢!

4

5 回答 5

4

使用abc 模块。具体来说,将基类的元类设置为ABCMeta并在方法上使用@abstractmethod装饰器cleanup

关于这是否是“pythonic”的争论是分裂的。描述该标准的PEP 3119列出了一些优点和缺点(但显然有利于 ABC)。它进入了标准库,这很好地表明许多人认为它在某些情况下很有用。在你的情况下,我认为这是合适的。

于 2010-09-24T01:41:57.163 回答
1

如果要确保实现了清理方法,可以用@abc.virtualmethod装饰器进行包装。这将导致在实例化任何未覆盖虚拟方法的对象时出错。这也要求您制作abc.ABCMeta班级的__metaclass__.

有关更多信息和一些示例,请参阅abc 模块

这并不常见:通常只会有文档说明实现者必须覆盖给定的方法。然而,这可能更多是由于abc模块的新颖性(Python 2.6 中的新功能),而不是这种方法的不符合 Python 的感觉。

于 2010-09-24T01:42:26.177 回答
1

为什么不让方法在被调用时cleanup引发?NotImplementedError如果他们希望您的孩子的课程能够正常工作,他们将不得不实施某种实施。

于 2010-09-24T01:46:17.073 回答
0

由于您没有可用的 abc,因此您可以使用简单的元类来执行此操作

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

该表达式type(db.Model)解析为使用的任何元类,db.Model因此我们不会踩谷歌的代码。此外,我们_abstract_methods_在类字典被传递给谷歌的方法之前弹出类字典,__new__这样我们就不会破坏任何东西。如果db.Model已经有一个具有该名称的属性,那么您需要将其更改为其他名称。

于 2010-09-24T02:23:28.873 回答
0

尽管我从未亲自使用过它,但我看到了很多对Zope 接口的引用。这对你的任务来说可能是多余的,但它可能会给你一些方向。对于具有 Java 背景的人来说,它可能会感觉很舒服。

于 2010-09-25T04:50:28.907 回答