pythonic方法公开用户覆盖钩子

pythonic方法公开用户覆盖钩子,python,flask,Python,Flask,注:虽然我的特殊用途与烧瓶有关,但我认为问题更一般 我正在构建一个由用户定制的Flask web应用程序。例如,用户需要提供DatabaseInterface的一个具体子类,并可以将其添加到应用程序知道如何处理的某些ModelObject的列表中 向用户公开各种钩子并指示必需和可选状态的最佳方式是什么?”这里的“最佳”主要是指最“pythonic”的,或者“python用户最容易掌握的”,但是其他的标准,比如不会引起麻烦,当然值得一提 我考虑过的一些方法: 完全依赖文件 创建一个带有文档覆盖的

注:虽然我的特殊用途与烧瓶有关,但我认为问题更一般

我正在构建一个由用户定制的Flask web应用程序。例如,用户需要提供DatabaseInterface的一个具体子类,并可以将其添加到应用程序知道如何处理的某些ModelObject的列表中

向用户公开各种钩子并指示必需和可选状态的最佳方式是什么?”这里的“最佳”主要是指最“pythonic”的,或者“python用户最容易掌握的”,但是其他的标准,比如不会引起麻烦,当然值得一提

我考虑过的一些方法:

  • 完全依赖文件

  • 创建一个带有文档覆盖的模板文件,就像许多服务器的默认配置文件一样。例如

    app = mycode.get_app()
    ##Add your list of extra foo classes here
    #app.extra_foos = []
    
  • 为每个钩子创建一个带有attr/方法的UserOverrides类;可能分为所需的覆盖和可选覆盖

  • 使用未实现的方法创建空类,用户必须将其子类化为具体实例
    • 一种方法是使用。例如,您可以使用抽象方法定义ABC,这些抽象方法必须由以下子类覆盖:

      from abc import ABC
      
      
      class MyClass(ABC):  # inherit from ABC
      
         def __init__(self):
            pass
      
          @abstractmethod
          def some_method(self, args):
              # must be overridden by child class
              pass
      
      然后,您将实现一个子类,如:

      class MyChild(MyClass):
          # uses parent's __init__ by default
          def some_method(self, args):
              # overrides the abstract method
      
      您可以使用文档指定在重写的方法中需要执行的所有操作。还有用于抽象属性、类方法和静态方法的装饰器。尝试实例化未覆盖其所有抽象方法/属性的ABC将导致错误。

      一个方法是使用。例如,您可以使用抽象方法定义ABC,这些抽象方法必须由以下子类覆盖:

      from abc import ABC
      
      
      class MyClass(ABC):  # inherit from ABC
      
         def __init__(self):
            pass
      
          @abstractmethod
          def some_method(self, args):
              # must be overridden by child class
              pass
      
      然后,您将实现一个子类,如:

      class MyChild(MyClass):
          # uses parent's __init__ by default
          def some_method(self, args):
              # overrides the abstract method
      
      您可以使用文档指定在重写的方法中需要执行的所有操作。还有用于抽象属性、类方法和静态方法的装饰器。试图实例化未覆盖其所有抽象方法/属性的ABC将导致错误。

      在Python中尤其如此,这为您提供了一个避免该问题的良好先例。考虑下面的代码:

      len({1,2,3}) # set with length 3
      len([1,2,3]) # list with length 3
      len((1,2,3)) # tuple with length 3
      
      这对于内置的数据结构来说很酷,但是如果您想创建自己的数据结构并让它与Python的
      len
      配合使用,该怎么办?简单:

      class Duple(object):
          def __init__(self, fst, snd):
              super(Duple, self).__init__()
              self.fst = fst
              self.snd = snd
      
          def __len__():
              return 2
      
      Duple
      是一种两元素(仅限)数据结构(使用更多或更少的参数调用),现在可用于
      len

      len(Duple(1,2)) # 2
      
      这正是你应该做的:

      def foo(arg):
          return arg.__foo__()
      
      任何想要使用
      foo
      函数的类都只需实现
      \uuuuuuuuuuuuuuuuuu
      魔术方法,这就是
      len
      在引擎盖下的工作方式。

      在Python中尤其如此,这为您提供了一个避免该问题的良好先例。考虑下面的代码:

      len({1,2,3}) # set with length 3
      len([1,2,3]) # list with length 3
      len((1,2,3)) # tuple with length 3
      
      这对于内置的数据结构来说很酷,但是如果您想创建自己的数据结构并让它与Python的
      len
      配合使用,该怎么办?简单:

      class Duple(object):
          def __init__(self, fst, snd):
              super(Duple, self).__init__()
              self.fst = fst
              self.snd = snd
      
          def __len__():
              return 2
      
      Duple
      是一种两元素(仅限)数据结构(使用更多或更少的参数调用),现在可用于
      len

      len(Duple(1,2)) # 2
      
      这正是你应该做的:

      def foo(arg):
          return arg.__foo__()
      

      任何想要使用
      foo
      函数的类都只实现了
      \uuuufoo\uuucode>魔术方法,这就是
      len
      在后台工作的方式。

      继承是一种蹩脚的扩展机制。使用对其参数调用魔术方法的函数,并让扩展类实现适当的dunder方法。@JaredSmith我恐怕大部分是希腊语。你愿意把它扩展成一个答案吗?因为现在唯一的答案是相反的?完成了。看看我下面的答案。继承是一种糟糕的扩展机制。使用对其参数调用魔术方法的函数,并让扩展类实现适当的dunder方法。@JaredSmith我恐怕大部分是希腊语。你愿意把它扩展成一个答案吗?因为现在唯一的答案是相反的?完成了。查看我下面的答案。2个问题:(1)定义新的双下划线方法不被认为是不可以的吗?(2)我可能遗漏了一些东西,但这似乎只是部分回答了我的问题。我说“传递一个实现这些方法的对象”,而不是告诉我的用户“将这个ABC子类化并传递你的具体实例”。但特别是如果某些方法是允许的默认值,拥有一个可重载的基类似乎比从头创建对象要省工。它并没有回答“我如何才能最好地传达哪些方法是可重载的?”@thegreatemu首先:与什么相比?遗产当互联网上到处都是关于遗产的罪恶的无数和五篇文章时?第二:看到第一个问题的答案,重新思考你的设计。遗传天生脆弱。在您的情况下,您可能可以不受影响(在这种情况下,另一个答案很好),但Python为您提供了其他工具。或者把你的问题转过来:为什么你认为Guido以这种方式实现
      len
      ,而不是简单地将其作为这些对象上的一种方法呢?PEP 8明确地说“
      \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu“位于用户控制的命名空间中的对象或属性。例如,“初始化”、
      “导入”、
      “文件”
      。永远不要捏造这样的名字;只在文档中使用它们。“@thegreatemu对相关但不重复的问题可能会有所帮助。对于PEP 8,很好,不要使用双下划线。但同样的原则也适用:使用duck类型,而不是继承。2个问题:(1)定义新的双下划线方法不被认为是不可以的吗?(2)我可能遗漏了一些东西,但这似乎只是部分回答了我的问题,而不是告诉我的用户“将这个ABC和pas子类化”