超越Python中的工厂模式

7 投票
6 回答
1595 浏览
提问于 2025-04-16 03:19

我之前用Java,现在转到Python,听说工厂模式在Python里不太“Pythonic”。所以,我想找一种更符合Python风格的方法来做一些事情。(我把目标简化了一下,这样就不用描述我整个复杂的程序了)。

我的脚本会读取一些人的名字(还有他们的一些信息),然后根据这些信息创建Person类型的对象。名字可能会重复,我只想为每个名字创建一个Person实例。这些人还可能属于Man和Woman这两个子类。

一种方法是创建一个PersonFactory,这个工厂要么返回一个新创建的Man或Woman,要么返回一个已经存在的同名Man/Woman的引用。另一种方法是创建一个包含所有Person对象的集合,每次在创建新对象之前都检查一下是否已经有同名的Person。不过这两种方法我觉得都不太符合Python的风格。第一种方法在Python里感觉有点繁琐(为了创建另一个对象而创建一个完整的类?真的有必要吗?),而第二种方法处理起来会很费劲,因为我有很多名字要处理。

6 个回答

2

__new__方法里放一个“不能有两个对象用相同的键”注册是个好主意,像这样:

class Person(object):
    person_registry = {}
    mens_names = set('Tom Dick Harry'.split())
    womens_names = set('Mary Linda Susan'.split())
    gender = "?"
    def __new__(cls, *args):
        if cls is Person:
            fname,lname = args[0].split()
            key = (lname, fname)
            if key in Person.person_registry:
                return Person.person_registry[key]

            if fname in Person.mens_names:
                return Man(*args)
            if fname in Person.womens_names:
                return Woman(*args)
        else:
            return object.__new__(cls, *args)

    def __init__(self, name):
        fname,lname = name.split()
        Person.person_registry[(lname, fname)] = self

class Man(Person):
    gender = "M"

class Woman(Person):
    gender = "W"

p1 = Person("Harry Turtledove")
print p1.__class__.__name__, p1.gender

p2 = Person("Harry Turtledove")

print p1 is p2

打印的结果是:

Man M
True

我也尝试了一下你提到的男女区分,但我对这个结果不是很满意。

13

我觉得工厂模式在Python中并不是不合适的。不过,你不一定需要创建一个完整的类。在Python中,有一个很大的不同就是你可以在类外面写代码。所以你可以考虑写一个工厂函数。或者,你也可以把工厂方法放在Person类里面,作为一个类的方法:

class Person:

    name_map = {}

    @classmethod
    def person_from_name(cls, name):
        if name not in cls.name_map:
            cls.name_map[name] = cls(name)
        return cls.name_map[name]

    def __init__(self, name):
        etc...

在Python代码中,很多时候和Java中的模式是一样的,但我们不会那么强调。在Java中,你需要创建一个全新的类,这通常意味着要新建一个.java文件,还得把它设为单例等等。Java似乎总是让事情变得复杂。其实一个简单的类方法就可以解决问题,所以直接用它就行了。

撰写回答