python工厂函数最佳实践


30

假设我有一个foo.py包含类的文件Foo

class Foo(object):
   def __init__(self, data):
      ...

现在,我想添加一个函数,该函数Foo以某种方式从原始源数据创建对象。我应该将其作为Foo中的静态方法还是另一个单独的函数?

class Foo(object):
   def __init__(self, data):
      ...
# option 1:
   @staticmethod
   def fromSourceData(sourceData):
      return Foo(processData(sourceData))

# option 2:
def makeFoo(sourceData):
   return Foo(processData(sourceData))

我不知道方便用户使用是否更重要:

foo1 = foo.makeFoo(sourceData)

还是在方法和类之间保持清晰的耦合更重要:

foo1 = foo.Foo.fromSourceData(sourceData)

Answers:


45

选择应该在工厂功能和第三个选项之间进行;类方法:

class Foo(object):
   def __init__(self, data):
      ...

   @classmethod
   def fromSourceData(klass, sourceData):
      return klass(processData(sourceData))

类方法工厂的另一个优点是它们是可继承的。现在,您可以创建的子类Foo,然后继承的工厂方法将生成该子类的实例。

在函数和类方法之间进行选择后,我将选择类方法。它很清楚地记录了工厂将要生产什么的对象,而不必在函数名称中明确指出。当您不仅具有多个工厂方法而且具有多个类时,这一点将变得更加清楚。

比较以下两种选择:

foo.Foo.fromFrobnar(...)
foo.Foo.fromSpamNEggs(...)
foo.Foo.someComputedVersion()
foo.Bar.fromFrobnar(...)
foo.Bar.someComputedVersion()

foo.createFooFromFrobnar()
foo.createFooFromSpamNEggs()
foo.createSomeComputedVersionFoo()
foo.createBarFromFrobnar()
foo.createSomeComputedVersionBar()

更好的是,最终用户可以只导入Foo类,并以各种创建类的方式使用它,因为您将所有工厂方法都放在一个位置:

from foo import Foo
Foo()
Foo.fromFrobnar(...)
Foo.fromSpamNEggs(...)
Foo.someComputedVersion()

stdlib datetime模块广泛使用类方法工厂,而它的API更为清晰。


好答案。有关更多信息@classmethod,请参见初学者的@classmethod和@staticmethod的含义?
nealmcb

非常关注最终用户功能
drtf

1

在Python中,与工厂方法相比,我通常更喜欢使用类层次结构。就像是:

class Foo(object):
    def __init__(self, data):
        pass

    def method(self, param):
        pass

class SpecialFoo(Foo):
    def __init__(self, param1, param2):
        # Some processing.
        super().__init__(data)

class FromFile(Foo):
    def __init__(self, path):
        # Some processing.
        super().__init__(data)

我将整个层次结构(仅此层次结构)放在一个模块中,例如foos.py。基类Foo通常实现所有方法(因此也实现接口),并且通常不由用户直接构造。子类是覆盖基本构造函数并指定如何Foo构造的一种方法。然后,我将Foo通过调用适当的构造函数来创建一个,例如:

foo1 = mypackage.foos.SpecialFoo(param1, param2)
foo2 = mypackage.foos.FromFile('/path/to/foo')

我发现它比从静态方法,类方法或模块级函数构建的工厂更加优雅和灵活。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.