【问题标题】:How to require implementation of method in Python?如何要求在 Python 中实现方法?
【发布时间】:2010-09-24 01:22:18
【问题描述】:

我在 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_activeis_spamis_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 的做事方式。

谢谢!

【问题讨论】:

    标签: python duck-typing abstract-methods


    【解决方案1】:

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

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

    【讨论】:

    • 我认为它在 app-engine 上不可用。它是在 2.6 中引入的,而我上次听说的 app-engine 是在 2.5
    • 啊,我不知道。在这种情况下,旧方法(只需记录它)可能就足够了。
    • @AaronMcSmooth:当有人提议在一个项目中使用 GAE 时,我害怕被谷歌决定公开的任何旧版本所困,所以没有使用它——如果它真的还在的话在 2010 年(甚至 2009 年)的 2.5 上,我很高兴我避免了它。
    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 效果很好,除非重写方法要调用super
      • @AaronMcSmooth 我认为这就是重点。 @intuited 这是真的。
      • 在子类化执行此操作的类时,我不能使用协作多重继承,因为它破坏了 super。您调用 super 以便运行 sibling 类的方法。正确的做法是使用pass 语句定义方法。
      【解决方案4】:

      由于你没有可用的 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_ 传递给 google 的 __new__ 方法之前,我们从类字典中弹出 __new__,这样我们就不会破坏任何东西。如果db.Model 已经有一个具有该名称的属性,那么您需要将其更改为其他名称。

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          猜你喜欢
          • 2011-06-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多