代码之家  ›  专栏  ›  技术社区  ›  chimeracoder

超越巨蟒工厂

  •  7
  • chimeracoder  · 技术社区  · 16 年前

    从爪哇来到Python,我被告知工厂不是蟒蛇。因此,我在寻找 python的方法如下。(我过于简单化了我的目标,所以我不必描述我的整个程序,这非常复杂)。

    我的脚本将读取人员的姓名(以及有关人员的一些信息),并由此构造Person类型的对象。名称可以重复,每个名称只需要一个Person实例。这些人也可能属于男人和女人的亚类。

    这样做的一种方法是创建一个PersonFactory,它可以返回一个新实例化的男人或女人。 对具有相同名称的先前实例化的男/女的引用。另一种方法是创建一组所有Person对象,并在实例化新对象之前,每次检查是否存在具有给定名称的人。不过,这两种方法对我来说都不是蟒蛇。第一个对于Python来说似乎有点麻烦(创建一个完整的类只是为了处理另一个对象的创建?真的吗?)第二个会很快变得昂贵,因为我有很多名字要处理。

    6 回复  |  直到 10 年前
        1
  •  12
  •   Ned Batchelder    16 年前

    我不认为工厂是非蟒蛇。不过,你不需要整堂课。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似乎滋生了这种复杂性。一个简单的类方法就可以了,所以只需使用它。

        2
  •  2
  •   Alex Martelli    16 年前

    独立功能 def PersonFactory(name, gender): 很好,尽管按照@ned的建议将其打包为类方法不应该有什么影响(在这种特殊情况下,它也不会有太大帮助,因为要实例化的人的确切类必须有所不同)。我认为最干净的实现实际上是一个独立的函数,仅仅是因为我更喜欢ClassMethod返回它调用的类的实例(而不是其他类的实例),但是这是一个风格点,不能说这两种方法都是明确定义的。

    我会对它进行编码(我希望有一些假设是明确的,例如性别编码为 M F 如果未指定,则从名称中启发式推断出&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
    
        3
  •  2
  •   PaulMcG    16 年前

    登记“两个不带同一钥匙的物体”的地方在 __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
    

    我还刺穿了你对男女的区别,但我对此并不感到兴奋。

        4
  •  2
  •   Amber    16 年前
    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中调用存储类类型之类的操作,因此不需要工厂;您只需查找并实例化一个类型即可。

        6
  •  0
  •   Veky    10 年前

    最简单的方法可能是在 __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()}