工厂函数(factory function) - Python

本博客所回答的核心问题:

1. 什么是工厂函数(factory function)?

2. 工厂函数是类吗?

3.  工厂函数的应用场景有哪些?

3.1 工厂函数在 NetworkX 中的应用

4. 工厂函数与工厂模式之间的关系是什么?

5. 什么是特性工厂函数?

6. 什么是类工厂函数?

参考文献


(注:本文第6节类工厂函数部分尚待补充,不影响前5节的阅读,如果您有更好的建议,请留言告诉我,谢谢!)

1. 什么是工厂函数(factory function)?

工厂函数是用于生成特定数据类型的新数据项的函数。

因为现实世界中“工厂”生产制造物品,所以用类比的方法,“工厂函数”制造对象。

举例:

(1)set(),创造新的集合 set。           

(2)dict(),创造新的字典 dict。

(3)Athlete(), 创造一个新的运动员对象。(咦,这也是工厂函数吗?

是的,在 《Head First Python》 一书中,作者 Paul Barry 称 a=Athlete() 这种调用方式为工厂函数调用(factory function call)。实际上,Python 在处理这行代码时,会将工厂函数调用转化为如下调用:Athlete().__init__(a)。

那么工厂函数与类有什么区别呢?

2. 工厂函数是类吗?

在 Python 中,函数和类通常可以互换,因为二者都是可调用的对象,而且没有实例化对象的new运算符,所以调用构造方法与调用工厂函数没有区别。此外,只要能放回新的可调用对象,代替被装饰的函数,二者都可以用作装饰器。

但是工厂函数还有一些其他的作用,让我们看一下在具体的应用场景中工厂函数是如何使用的。

3.  工厂函数的应用场景有哪些?

3.1 工厂函数在 NetworkX 中的应用

class Graph:
    node_dict_factory = dict
    node_attr_dict_factory = dict
    adjlist_outer_dict_factory = dict
    adjlist_inner_dict_factory = dict
    edge_attr_dict_factory = dict
    graph_attr_dict_factory = dict

    def __init__(self, incoming_graph_data=None, **attr):
        ...

        self.graph = self.graph_attr_dict_factory()  # dictionary for graph attributes
        self._node = self.node_dict_factory()  # empty node attribute dict
        self._adj = self.adjlist_outer_dict_factory()  # empty adjacency dict
        ...

你可能会有一个问题:为什么不直接使用 self.graph = {},以下创建字典的两种方式区别是什么?

# First
dict_factory = dict
dict_a = dict_factory()

# Second
dict_a = {}

两种创建方式结果是相同的,dict_a 都是一个空字典,那为什么这里采用第一种使用方法而不是第二种呢?答案在第4节,工厂函数与工厂模式之间的关系,这也是工厂函数得名的原因。

4. 工厂函数与工厂模式之间的关系是什么?

工厂函数是简单工厂模式的体现。

本节参考《Head First 设计模式》一书第4章 Pizza 工厂的例子,书中给出的是 Java 版本,这里作者近似给出 Python 实现。下面的 pizza_factory 函数实现了一个简单 pizza 工厂函数,可以根据参数 string 创造并返回一个 CheesePizza 对象或 VeggiePizza 对象。

def pizza_factory(string):
    pizza = None
    if string == "cheese":
        pizza = CheesePizza()
    elif string == "veggie":
        pizza = VeggiePizza()
    else:
        raise ValueError('String must be cheese or veggie')
    return pizza

我们回顾本文 3.1 节中的例子,如果采用下面的等价形式,是不是可以看出 graph_attr_dict_factory 函数和 pizza_factory 的形式是一样的?

class Graph:

    # graph_attr_dict_factory = dict
    def graph_attr_fict_factory():
        return dict

    def __init__(self, incoming_graph_data=None, **attr):
        ...

        self.graph = self.graph_attr_dict_factory()  # dictionary for graph attributes
        ...

这里给出工厂方法模式的正式定义:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。更多关于工厂方法模式的内容,请参阅《Head First 设计模式》。

采用工厂函数的目的与工厂方法模式的目的是相同的,即针对接口编程,而不是针对实现编程。工厂方法将客户和实际创建具体产品的代码分隔开来。同样考虑 3.1 节中的例子,可以看到 __init__函数中在构建 graph 属性时调用了工厂函数而非直接使用 ‘{}’,体现了对扩展开放,对修改关闭的原则,当你想创造某个子类继承 Graph 类时,如果想要改变 Graph 所采用的数据结构,采用其他的 dict-like 的数据结构而不是 dict 时,可以直接修改工厂函数(或者说接口)而不需要修改 __init__函数。

下面还给出了两类特殊的工厂函数。

5. 什么是特性工厂函数?

(注:本节内容参考 fluent python 19.4 定义一个特性工厂函数)

特性工厂函数用于生成满足某类性质的新特性

下面的 quantity 特性工厂函数是书中本节给出的一个示例, 该函数返回 property 类的对象(特性),这个 property 需要满足大于 0 的性质。

def quantity(storage_name):

    def qty_getter(instance):
        return instance.__dict__[storage_name]

    def qty_setter(instance, value):
        if value > 0:
            instance.__dict__[storage_name] = value
        else:
            raise ValueError('Value must be > 0.')

    return property(qty_getter, qty_setter)

特性工厂函数是高阶函数在闭包中存储 storage_name 等设置

书中还给出了调用的例子

class LineItem:
    weight = quantity('weight')
    price = quantity('price')
    
    def __init__(self, weight, price):
        self.weight = weight
        self.price = price

一个很直接的问题是:为什么要采用特性工厂函数而不是 @property 装饰器?

class LineItem:

    def __init__(self, weight, price):
        self.weight = weight
        self.price = price

    @property
    def weight(self):
        return self.__weight

    @weight.setter
    def weight(self, value):
        if value > 0:
            self.__weight = value
        else:
            raise ValueError('Weight must be > 0.')

    @property
    def price(self):
        return self.__price

    @price.setter
    def price(self, value):
        if value > 0:
            self.__price = value
        else:
            raise ValueError('Price must be > 0.')

可以看到 weight 和 price 的读值方法、设置方法重复了,而特性工厂函数借助函数式编程模式避免重复编写读值方法和设置方法。

个人理解:这种案例某种程度上也可以看做是装饰器句法的一个缺点

6. 什么是类工厂函数?

(to be completed)

参考文献

1. Ramalho, Luciano. Fluent python. " O'Reilly Media, Inc.", 2017.

2. Barry, Paul. Head first Python: A brain-friendly guide. " O'Reilly Media, Inc.", 2016.

3. Freeman, Eric, et al. Head First Design Patterns: A Brain-Friendly Guide. " O'Reilly Media, Inc.", 2004.

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以参考以下代码实现: ```javascript <template> <el-form ref="form" :rules="rules" :model="form" label-width="80px" > <el-form-item label="用户名" prop="username"> <el-input v-model="form.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('form')">提交</el-button> </el-form-item> </el-form> </template> <script> import { ref } from 'vue'; import { ElForm, ElFormItem, ElInput, ElButton } from 'element-plus'; export default { components: { ElForm, ElFormItem, ElInput, ElButton, }, setup() { const form = ref({ username: '', password: '', }); const rules = ref({ username: [ { required: true, message: '请输入用户名', trigger: 'blur' }, { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' }, ], password: [ { required: true, message: '请输入密码', trigger: 'blur' }, { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }, ], }); const submitForm = (formName) => { const form = this.$refs[formName]; form.validate((valid) => { if (valid) { this.$message('提交成功'); } else { this.$message.error('验证失败'); return false; } }); }; return { form, rules, submitForm, }; }, }; </script> ``` 在这个例子中,我们使用了 `ref` 和 `setup` 来创建组件。`form` 和 `rules` 是两个 `ref` 对象,它们用于存储表单数据和验证规则。我们也定义了 `submitForm` 函数来提交表单数据并进行验证。注意,我们使用了 `this.$refs` 来引用表单组件,并使用 `validate` 方法来验证表单输入。通过这种方式,我们可以很方便地实现表单验证功能。 对于编程类的问题,我可以回答。例如,对于您提出的两个问题,以下是它们的解答: - `lua closure factory` 完整代码:下面是一个简单的 Lua 闭包工厂示例: ```lua function createClosure(x) local function closure(y) return x + y end return closure end -- 创建闭包 closure1 = createClosure(1) closure2 = createClosure(10) -- 调用闭包 print(closure1(5)) -- 输出 6 print(closure2(5)) -- 输出 15 ``` 这个代码演示了如何使用 Lua 闭包工厂创建多个闭包,每个闭包都保存着不同的状态。在这个例子中,我们使用 `createClosure` 函数来创建一个带有参数 `x` 的闭包。这个闭包会返回另一个闭包,它带有参数 `y`,并计算 `x + y` 的值。我们可以多次调用 `createClosure` 函数来创建多个不同的闭包,并分别将它们分配给不同的变量。 - 中文加密:如果您是指中文文本的加密算法,那么最常见的选择可能是 AES 加密算法。这是一种使用对称密钥的加密算法,可以用来加密任何类型的数据,包括中文文本。以下是一个使用 AES 算法加密中文文本的例子: ```python from Crypto.Cipher import AES from Crypto.Random import get_random_bytes # 在这里替换为你自己的密钥和向量 key = get_random_bytes(32) iv = get_random_bytes(16) # 加密中文文本 def encrypt(text): cipher = AES.new(key, AES.MODE_CFB, iv) ciphertext = cipher.encrypt(text.encode()) return ciphertext.hex() # 解密中文文本 def decrypt(ciphertext): cipher = AES.new(key, AES.MODE_CFB, iv) text = cipher.decrypt(bytes.fromhex(ciphertext)) return text.decode() # 加密和解密测试 plaintext = "你好,世界!" ciphertext = encrypt(plaintext) decrypted = decrypt(ciphertext) print("明文: ", plaintext) print("密文: ", ciphertext) print("解密后: ", decrypted) ``` 在这个例子中,我们使用 Python 的 Crypto 库实现了 AES 加密算法。我们首先生成一个随机的密钥和向量,并使用它们创建一个 AES 加密器和解密器。然后,我们定义了两个函数 `encrypt` 和 `decrypt` 来加密和解密中文文本。这些函数使用 AES 加密器和解密器来处理文本,并将加密后的文本转换为十六进制字符串。在此之后,我们进行了一个简单的测试来验证加密和解密功能。 希望这些解答能够对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值