在Python中超越工厂

从Java进入Python,我被告知工厂不是Pythonic。 因此,我正在寻找一种 Python方式来执行以下操作。 (我过分简化了我的目标,所以我不必描述我的整个程序,这非常复杂)。

我的脚本将读取人的名字(以及有关它们的一些信息),并从中构建Person类型的对象。 名称可能会重复,我只需要每个名称一个Person实例。 这些人也可能属于男人和女人的子类。

一种方法是创建一个PersonFactory,它可以返回一个新实例化的Man或Woman, 可以引用之前实例化的具有相同名称的Man / Woman。 另一种方法是创建一组所有Person对象,并在实例化新对象之前每次检查是否存在具有给定名称的Person。 然而,这两种方法都不像Pythonic那样让我感到震惊。 对于Python来说,第一个似乎有点过于繁琐(创建一个完整的类只是为了处理另一个对象的创建?真的吗?)而第二个会很快变得昂贵,因为我有很多要处理的名字。

我不认为工厂不是Pythonic。 但是,你不需要整个class级。 Java和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似乎会产生这种复杂性。 一个简单的类方法就可以了,所以只需使用它。

一个独立的函数def PersonFactory(name, gender):很好,虽然把它打包成类方法,正如@Ned建议的那样,不应该受到伤害(在这种特殊情况下它也没有多大帮助,因为确切的类实例化的人必须变化)。 我认为最干净的实现实际上是一个独立的函数,只是因为我更喜欢使用classmethod来返回它所调用的类的实例(而不是其他类的实例) – 但这是一个不可能的风格点。据说两种方式都有明确的定义。

我给它编码(有一些假设,我希望这些假设很清楚,例如,性别被编码为MF ,如果没有指定是从名称推断出来的,&c):

 def gender_from_name(name): ... person_by_name = {} class_by_gender = {'M': Man, 'F': Woman} def person_factory(name, gender=None): p = person_by_name.get(name) if p is None: if gender is None: gender = gender_from_name(name) p = person_by_name[name] = class_by_gender[gender](name) return p 

放置“没有两个具有相同密钥的对象”注册的地方在__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 

我也抨击了你的男人/女人的区别,但我对此并不感到兴奋。

 class Person(object): # ... class Man(Person): # ... class Woman(Person): # ... constructors = { 'male': Man, 'female': Woman, None: Person, } people = {} # in processing loop if person.name not in people: people[person.name] = constructors[person.gender]() person_object = people[person.name] 

由于Python允许你做一些事情,比如在dict中调用存储类类型,你不需要工厂; 你可以只查找一个类型并实例化它。

Python中的Flyweight模式 ? (似乎破了, 互联网档案版 )

编辑: Python中的另一个flyweight示例 。

最简单的方法可能是在__new__上使用来自functools的内置__new__

 import functools class Person: gender = 'unknown' @functools.lru_cache(maxsize=None) def __new__(cls, full_name): names = first, last = full_name.split() for subclass in cls.__subclasses__(): if first in subclass.names: cls = subclass self = super().__new__(cls) self.first, self.last = names return self class Woman(Person): gender = 'female' names = {*'Mary Linda Susan'.split()} class Man(Person): gender = 'male' names = {*'Tom Dick Harry'.split()}