c

codeye

V1

2022/10/22阅读:19主题:默认主题

用于可扩展、可重用和优雅的代码的Python工厂

用于可扩展、可重用和优雅的代码的Python工厂

通过将行为(在工厂超类中定义)与对象创建(在子类中)解耦,创建灵活、可扩展的代码。

在现实生活中的工厂中,相同或相似对象的生产不是单独完成的,而是在流水线上精简的。同样地,工厂设计模式允许你创建的软件不与特定的产品相联系,而是可以在许多类似的应用中重复使用。工厂模式是著名的Gof(四人帮)设计模式之一[1]。它是一种创造型的设计模式,它将对象的创建封装在一个叫做工厂的类中。这种模式通过与系统解耦来促进系统的灵活性,即如何、何时以及由谁来创建其对象[6]。

当你需要创建新类型的对象时,工厂就特别有用。如果对象的创建分散在整个代码中,那么你将不得不花时间去寻找代码中对象类型的确切位置[2]。在软件工程方面,这意味着你有大量的技术债务需要处理。这在机器学习中尤其如此,正如[4]中所解释的那样。机器学习会产生巨大的维护成本,这源于数据依赖、数据漂移和隐藏的反馈回路。最近,通过创建特征商店[3][5],工厂已经在机器学习中找到了自己的方式。特征库可以是在线的,也可以是离线的,其主要目的是抽象数据转换,因为它们适用于不同的特征,并通过这种方式实现更快的开发,更好的协作,有效的模型部署和扩展[7]。

1.0 在Python中实现工厂的一个例子

现在让我们来看一个如何在Python中使用工厂的具体例子。假设我们想为一个刚从大学毕业的儿子创建一个新的衣柜,他在一家银行找到了第一份工作,是一个定量金融分析师。鉴于他精通Python,他决定实现一个虚拟衣柜的索引。我们将帮助他为每件衣服分配一个 "正式等级",因为他完全有能力穿着T恤和破洞牛仔裤出现在一个重要的会议上。

如下图所示,虚拟衣橱索引从创建两个抽象工厂开始, 一个是鞋类(FootWear) 一个是衣服(MensClothes)

这些都是抽象类,它们只起到模板的作用。与 Java 不同,Python 没有一个固有的抽象类类型。相反,它提供了一个叫做 abc 的模块,它提供了抽象类的基础结构。

MensClothes 类的 colortcolorb 变量分别代表上衣 (衬衫、毛衣等) 和下衣 (裤子) 的颜色。注意,我们给鞋子和上下装的颜色分配了一个默认值 "棕色"(安全的颜色)。

import ast
from abc import ABC

class FootWear(ABC):
    def shoespecs(self, c="Brown"):
        self.color = c


class MensClothes(ABC):
    def __init__(self, rt="Brown", rb="Brown"):
        self.colort = rt
        self.colorb = rb

    def clothespecs(self, foot_w):
        pass

现在让我们通过定义具体的类BootsLoafers来实现两种类型的鞋类。我们继承自FootWear类,然后覆盖其shoespecs()方法来实现每个具体类的具体动作。

class Boots(FootWear):
    def __init__(self):
        print("Wear boots on a cold day")

    def shoespecs(self, c="Brown"):
        print(f"You picked {c} boots")


class Loafers(FootWear):
    def __init__(self):
        print("Shoes for a nice day")

    def shoespecs(self, c="Brown"):
        print(f"You picked {c} loafers")

同样地,我们创建了两个继承自MensClothes的具体类:SweaterAndCurdoroyPants,和ButtonDownShirtAndDressPants

每个具体的类都重写了抽象类的方法 clothespecs(),以便为每个样式指定特定的内容。请注意,clothespecs()方法接受一个FootWear对象,以使鞋类的颜色与裤子的颜色相匹配。

class SweaterAndCorduroyPants(MensClothes):
    def __init__(self, rt="Brown", rb="Brown"):
        super().__init__(rt, rb)
        print(f"You picked the {rt} sweater and the {rb} corduroy pants")

    def clothespecs(self, foot_w):
        foot_w.shoespecs(c=self.colorb)


class ButtonDownShirtAndDressPants(MensClothes):
    def __init__(self, rt="Brown", rb="Brown"):
        super().__init__(rt, rb)
        print(f"You picked the {rt} button-down shirt and the {rb} dress pants ")

    def clothespecs(self, foot_w):
        foot_w.shoespecs(c=self.colorb)

下面的抽象类Outfit是一个工厂,它提供了选择衣服和鞋子的方法,分别是choose_clothes()choose_shoes(),并且有一个实例属性,formality

class Outfit(ABC):
    def __init__self(self):
        self.formality=0
        pass

    def choose_clothes(self):
        pass

    def choose_shoes(self):
        pass

下面的具体类LightColor_ColdAndCasualDay继承自抽象类Outfit,有以下方法。

(a) 一个构造函数,指定了形式和名称变量。形式变量是从Outfit继承的。

(b) Outfit类的choice_clothes()方法的重载形式,它返回一个SweaterAndCorduroyPants对象。

(c) Outfitchoice_shoes()方法的重载形式,该方法返回一个Boots对象。

正如我们所看到的,当我们向上移动类的层次时,事情变得更加具体,而较低级别的类,如FootWearMensClothesOutfit,对于创建什么子类、在哪里创建它们的对象以及它们如何被聚合都是不可知的。

下面的DarkColor_WarmSemiFormalDay类也定义了一种类似的结构,它定义了一些方法来返回衣服的ButtonDownShirtAndDressPants对象和鞋类的Loafers对象。

class LightColor_ColdAndCasualDay(Outfit):
    def __init__(self):
        self.formality = 1
        self.name = "Light Colored Outfit for a Cold and Casual Day"

    def choose_clothes(self):
        return SweaterAndCorduroyPants(rt="Cream-colored", rb="Light Brown")

    def choose_shoes(self):
        return Boots()


class DarkColor_WarmSemiFormalDay(Outfit):
    def __init__(self):
        self.formality = 2
        self.name = "Dark Colored Outfit for a Semi-Formal Day"

    def choose_clothes(self):
        return ButtonDownShirtAndDressPants(rt='burgundy',rb='black')

    def choose_shoes(self):
        return Loafers()
    

与到目前为止我们看到的抽象工厂相比,DarkColor_WarmSemiFormalDayLightColor_ColdAndCasualDay是具体的工厂。它们如何作为对象的工厂,在下面的Look类的定义中得到了说明。类Look是负责创建对象的协调类。

正如我们在下面看到的,它的构造函数需要一个工厂作为参数。在我们有限的衣柜里,这个工厂可以是LightColor_ColdAndCasualDayDarkColor_WarmSemiFormalDay对象。

当我们创建一个Look类的对象时,工厂的choose_clothes()choose_shoes()被调用,它们分别给实例变量closho赋值。

然后,这些变量被用于重要的dress()方法,该方法将所有东西放在一起并创建完整的外观!这是一个可扩展、可重复使用的方法。这是可扩展的、可重复使用的代码,因为战略性地放置了对象创建的封装和多态性。

class Look:
    def __init__(self, factory):
        self.factory = factory
        self.name = factory.name
        print(factory.name)
        self.clo = factory.choose_clothes()
        self.sho = factory.choose_shoes()
        self.formality = factory.formality

    def dress(self):
        self.clo.clothespecs(self.sho)

    def __eq__(self, m):
        return self.formality == m.formality

    def __gt__(self, m):
        return self.formality > m.formality

在下面的代码片段中,我们可以看到我们的对象创建和礼服()方法的调用。请注意,我们检查了两个对象是否相等或对象g2是否大于对象g1。

g1 = Look(LightColor_ColdAndCasualDay())
g1.dress()
g2 = Look(DarkColor_WarmSemiFormalDay())
g2.dress()
print(g1 == g2)
print(g2 > g1

在上面Look类的定义中,我们把__eq__运算符的输出定义为两个对象的形式性的平等比较的输出。正如我们所讨论的,我们的新晋金融分析师对形式上的风格一无所知,所以我们需要以一种特定的方式(形式上的等级)告诉他,哪些外观在形式上被认为是相等的。

所以,3是最高的正式性(商务套装),0是没有正式性(T恤衫和运动鞋)。下面的等号操作返回假,大号操作返回真,因为g2的正式性等级(2)比g1(1)高。

最后,在下面的代码片段中,我们导入了当前的虚拟衣橱,它以字典的形式存在于一个文本文件中,并将我们创建的两个新外观附加到它上面,Outfit对象的名称作为键,形式感作为值。

with open('virtual_wardrobe.txt') as f:
    data = f.read()
wardrobe = ast.literal_eval(data)
print(wardrobe)

wardrobe[g1.name] = g1.formality
wardrobe[g2.name] = g2.formality
print(wardrobe)

with open('virtual_wardrobe.txt''w') as v_wardrobe:
    v_wardrobe.write(str(wardrobe))
v_wardrobe.close()

你可以在我的github目录中看到整个代码,作为一个Jupiter笔记本:https://github.com/theomitsa/Python-factories

谢谢您的阅读!

参考文献

Gamma, E., Helm, R., Johnson, and R., J. Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley Professional, Oct. 1994.

Eckel, B. et al., Python 3 Patterns, Recipes, and Idioms, Read the Docs (ebook) Fly, A., Feature Store: 数据科学工厂的组成部分,中。迈向数据科学》,2019年9月。

Sculley, D. et al., 机器学习。The High-Interest Credit Card of Technical Debt, Software Engineering for Machine Learning, NIPS Conference Workshop, 2014.

Taifi, M., ML Feature Stores: 休闲之旅1/3, Medium, 2020年4月。 Bishop, J, C# 3.0 Design Patterns, O'Reilly Media, p. 336, 2007.

Hirschtein, A., What Are Feature Stores And Why Are They Critical For Scaling Data Science? , Medium: 迈向数据科学》,2020年4月。

分类:

后端

标签:

后端

作者介绍

c
codeye
V1