繁体   English   中英

如何制作Python工厂类

[英]How to make a Python factory class

我希望能够基于枚举类创建对象,并使用字典。 像这样:

class IngredientType(Enum):
    SPAM        = auto() # Some spam
    BAKE_BEANS  = auto() # Baked beans
    EGG         = auto() # Fried egg

class Ingredient(object):
    pass    
class Spam(Ingredient):
    pass
class BakedBeans(Ingredient):
    pass
class Egg(Ingredient):
    pass


class IngredientFactory(object):
    """Factory makes ingredients"""

    choice = {
        IngredientType.SPAM: IngredientFactory.MakeSpam,
        IngredientType.BAKED_BEANS: IngredientFactory.MakeBakedBeans,
        IngredientType.EGG: IngredientFactory.MakeEgg
    }

    @staticmethod
    def make(type):
        method = choice[type]
        return method()

    @staticmethod
    def makeSpam():
        return Spam()

    @staticmethod
    def makeBakedBeans():
        return BakedBeans()

    @staticmethod
    def makeEgg():
        return Egg()

但是我得到了错误:

NameError: name 'IngredientFactory' is not defined

由于某些原因,无法创建字典。 我在哪里错了?

Python不是Java,不需要将所有内容都放在类中。 这里的IngredientFactory类没有状态,只有静态方法,因此它实际上只是一个单例名称空间,在python中,使用模块作为单例名称空间和普通函数可以正常完成此操作。 另外,由于Python类已经可以调用,因此将实例化包装在函数中没有任何意义。 简单,直接的pythonic实现是:

# ingredients.py

class IngredientType(Enum):
    SPAM        = auto() # Some spam
    BAKE_BEANS  = auto() # Baked beans
    EGG         = auto() # Fried egg

class Ingredient(object):
    pass    

class Spam(Ingredient):
    pass

class Beans(Ingredient):
    pass

class Egg(Ingredient):
    pass


_choice = {
        IngredientType.SPAM: Spam,
        IngredientType.BAKED_BEANS: Beans,
        IngredientType.EGG: Egg
    }

def make(ingredient_type):
    cls = _choice[ingredient_type]
    return cls()

和客户端代码:

import ingredients
egg = ingredients.make(ingredients.IngredientType.EGG)

# or much more simply:

egg = ingredients.Egg()

FWIW IngredientType枚举在这里并没有带来太多好处,甚至使事情变得更复杂,您必须使用纯字符串:

# ingredients.py

class Ingredient(object):
    pass    

class Spam(Ingredient):
    pass

class Beans(Ingredient):
    pass

class Egg(Ingredient):
    pass


_choice = {
        "spam": Spam,
        "beans": Beans,
        "egg": Egg
    }

def make(ingredient_type):
    cls = _choice[ingredient_type]
    return cls()

和客户端代码:

import ingredients
egg = ingredients.make("egg")

或者,如果您真的想使用Enum ,则至少可以通过使用类本身作为MadPhysicist建议的枚举值来摆脱choices字典:

# ingredients.py

class Ingredient(object):
    pass    

class Spam(Ingredient):
    pass

class Beans(Ingredient):
    pass

class Egg(Ingredient):
    pass

class IngredientType(Enum):
    SPAM = Spam
    BEANS = Beans
    EGG = Egg

    @staticmethod
    def make(ingredient_type):
        return ingredient_type.value()

和客户端代码

 from ingredients import IngredientType
 egg = IngredientType.make(IngredientType.EGG)

但我真的也看不到任何好处

编辑:您提到:

我试图实现工厂模式,以隐藏对象的创建。 然后,工厂的用户无需了解具体类型就可以处理“成分”

用户仍然必须指定他想要哪种食材( ingredient_type参数),因此我不确定我在这里是否了解好处。 实际的用例是什么? (编造/精简示例的问题在于它们无法说明全部内容)。

将您的映射放置在类的末尾,并直接引用方法,因为它们位于同一名称空间中:

choice = {
    IngredientType.SPAM: makeSpam,
    IngredientType.BAKED_BEANS: makeBakedBeans,
    IngredientType.EGG: makeEgg
}

直到类主体中的所有代码都不会创建类对象,因此您无法访问类本身。 但是,由于类主体是在专用名称空间中处理的,因此您可以访问到此为止定义的任何属性(这就是为什么必须在映射末尾进行映射的原因)。 还要注意,虽然您可以访问全局变量和内置插件,但不能访问封闭类或函数的名称空间。

这是来自官方文档的详细但仍介绍性的解释,说明了如何执行类: https : //docs.python.org/3/tutorial/classes.html#a-first-look-at-classes

看了布鲁斯·埃克尔的书后,我想到了:

#Based on Bruce Eckel's book Python 3 example
# A simple static factory method.
from __future__ import generators
import random
from enum import Enum, auto

class ShapeType(Enum):
    CIRCLE  = auto() # Some circles
    SQUARE  = auto() # some squares

class Shape(object):
    pass

class Circle(Shape):
    def draw(self): print("Circle.draw")
    def erase(self): print("Circle.erase")

class Square(Shape):
    def draw(self): print("Square.draw")
    def erase(self): print("Square.erase")

class ShapeFactory(object):

    @staticmethod
    def create(type):
        #return eval(type + "()") # simple alternative
        if type in ShapeFactory.choice:
            return ShapeFactory.choice[type]()

        assert 0, "Bad shape creation: " + type    

    choice = { ShapeType.CIRCLE:  Circle,
               ShapeType.SQUARE:  Square                
             }

# Test factory
# Generate shape name strings:
def shapeNameGen(n):

    types = list(ShapeType)

    for i in range(n):
        yield random.choice(types)

shapes = \
  [ ShapeFactory.create(i) for i in shapeNameGen(7)]

for shape in shapes:
    shape.draw()
    shape.erase()

这使用户可以从枚举中选择类类型,并阻止任何其他类型。 这也意味着用户不太可能写出带有拼写错误的“坏字符串”。 他们只是使用枚举。 然后,测试的输出如下所示:

Circle.draw
Circle.erase
Circle.draw
Circle.erase
Square.draw
Square.erase
Square.draw
Square.erase
Circle.draw
Circle.erase
Circle.draw
Circle.erase
Square.draw
Square.erase

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM