超越Python中的工厂模式
我之前用Java,现在转到Python,听说工厂模式在Python里不太“Pythonic”。所以,我想找一种更符合Python风格的方法来做一些事情。(我把目标简化了一下,这样就不用描述我整个复杂的程序了)。
我的脚本会读取一些人的名字(还有他们的一些信息),然后根据这些信息创建Person类型的对象。名字可能会重复,我只想为每个名字创建一个Person实例。这些人还可能属于Man和Woman这两个子类。
一种方法是创建一个PersonFactory,这个工厂要么返回一个新创建的Man或Woman,要么返回一个已经存在的同名Man/Woman的引用。另一种方法是创建一个包含所有Person对象的集合,每次在创建新对象之前都检查一下是否已经有同名的Person。不过这两种方法我觉得都不太符合Python的风格。第一种方法在Python里感觉有点繁琐(为了创建另一个对象而创建一个完整的类?真的有必要吗?),而第二种方法处理起来会很费劲,因为我有很多名字要处理。
6 个回答
Python中的享元模式?(似乎有问题,互联网档案馆版本)
在__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
我也尝试了一下你提到的男女区分,但我对这个结果不是很满意。
我觉得工厂模式在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似乎总是让事情变得复杂。其实一个简单的类方法就可以解决问题,所以直接用它就行了。