簡體   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