【问题标题】:Using base class constructor as factory in Python?在 Python 中使用基类构造函数作为工厂?
【发布时间】:2009-02-13 10:03:19
【问题描述】:

我使用基类构造函数作为工厂,并在这个构造函数/工厂中更改类以选择合适的类——这种方法是好的 python 实践还是有更优雅的方法?

我尝试阅读有关元类的帮助,但没有取得很大成功。

这里是我在做什么的例子。

class Project(object):
  "Base class and factory."
  def __init__(self, url):
      if is_url_local(url):
        self.__class__ = ProjectLocal
      else:
        self.__class__ = ProjectRemote
      self.url = url

class ProjectLocal(Project):
  def do_something(self):
    # do the stuff locally in the dir pointed by self.url

class ProjectRemote(Project):
  def do_something(self):
    # do the stuff communicating with remote server pointed by self.url

有了这段代码,我可以通过基类 Project 创建 ProjectLocal/ProjectRemote 的实例:

project = Project('http://example.com')
project.do_something()

我知道另一种方法是使用基于 url 返回类对象的结构函数,然后代码看起来相似:

def project_factory(url):
      if is_url_local(url):
        return ProjectLocal(url)
      else:
        return ProjectRemote(url)

project = project_factory(url)
project.do_something()

我的第一种方法只是出于品味还是有一些隐藏的陷阱?

【问题讨论】:

    标签: python factory


    【解决方案1】:

    您不应该为此需要元类。看看__new__ 方法。这将允许您控制对象的创建,而不仅仅是初始化,因此返回您选择的对象。

    class Project(object):
      "Base class and factory."
      def __new__(cls, url):
        if is_url_local(url):
           return super(Project, cls).__new__(ProjectLocal, url) 
        else:
           return super(Project, cls).__new__(ProjectRemote, url) 
    
      def __init__(self, url):
        self.url = url
    

    【讨论】:

    • 我想这就是我一直在寻找但迷失在元类中的东西。谢谢。
    【解决方案2】:

    我会坚持使用工厂函数方法。这是非常标准的python,易于阅读和理解。您可以通过多种方式使其更通用以处理更多选项,例如通过传入鉴别器函数和将结果映射到类。

    如果第一个示例有效,则更多是靠运气而不是设计。如果你想在你的子类中定义一个__init__ 怎么办?

    【讨论】:

    • +1:不要试图让基类做比成为基类更多的事情。工厂总是分开的。
    • 定义工厂的另一个不错的选择是类方法——它们通过cls 参数免费为您提供类参数化。
    【解决方案3】:

    以下链接可能会有所帮助: http://www.suttoncourtenay.org.uk/duncan/accu/pythonpatterns.html#factory http://code.activestate.com/recipes/86900/

    此外,当您使用新样式类时,使用 __new__ 作为工厂函数(而不是在基类中,单独的类更好)是通常所做的(据我所知)。

    工厂函数通常更简单(正如其他人已经发布的那样)

    此外,按照您的方式设置 __class__ 属性也不是一个好主意。

    希望您找到答案和链接。

    一切顺利。

    【讨论】:

    • +1, new 是执行此操作的“正确”方法,如果您真的必须这样做的话。不过不推荐给初学者。为了清晰起见,坚持使用工厂函数,或者重构类,以便本地/远程由类以外的东西(例如组合)决定。
    • 感谢您的提示。我无论如何都不是专家,所以我的答案只是基于我在书本上研究的内容。很高兴得到一些实用的建议。再次感谢。
    • 我确实有 Python 的实际经验。我喜欢在里面编程。但是,我对 Python 中的设计模式了解不多。
    【解决方案4】:

    是的,正如@scooterXL 所提到的,工厂函数是这种情况下的最佳方法,但我想指出工厂作为类方法的情况。

    考虑以下类层次结构:

    class Base(object):
    
        def __init__(self, config):
            """ Initialize Base object with config as dict."""
            self.config = config
    
        @classmethod
        def from_file(cls, filename):
            config = read_and_parse_file_with_config(filename)
            return cls(filename)
    
    class ExtendedBase(Base):
    
        def behaviour(self):
            pass # do something specific to ExtendedBase
    

    现在您可以从配置字典和配置文件创建 Base 对象:

    >>> Base({"k": "v"})
    >>> Base.from_file("/etc/base/base.conf")
    

    此外,您还可以免费使用 ExtendedBase:

    >>> ExtendedBase({"k": "v"})
    >>> ExtendedBase.from_file("/etc/extended/extended.conf")
    

    所以,这个类方法工厂也可以看作是辅助构造函数。

    【讨论】:

      【解决方案5】:

      我通常有一个单独的工厂类来执行此操作。这样您就不必使用元类或分配给self.__class__

      我也尽量避免将关于哪些类可用于创建的知识放入工厂。相反,我让所有可用的类在模块导入期间向工厂注册。给那里的类和一些关于何时向工厂选择此类的信息(这可以是名称、正则表达式或可调用的(例如注册类的类方法)。

      对我来说效果很好,还实现了封装和信息隐藏等功能。

      【讨论】:

        【解决方案6】:

        我认为使用工厂函数的第二种方法比让基类的实现依赖于它的子类要干净得多。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-03-23
          • 1970-01-01
          • 2021-02-07
          • 2012-01-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-11
          相关资源
          最近更新 更多