与下面实现对比一下:
- print pipeline_each(bands, [set_canada_as_country,
- strip_punctuation_from_name,
- capitalize_names])
这段代码很容易理解。给人的印象是辅助函数是函数式的,因为它们看过来是串联在一起的。前一个函数的输出成为下一个的输入。如果是函数式的,就很容易验证。也易于重用、易于测试且易于并行化。
pipeline_each() 的功能就是将乐队一次一个地传递给一个转换函数,比如 set_canada_as_country() 。将转换函数应用于所有乐队后, pipeline_each() 将转换后的乐队打包起来。然后,打包的乐队传递给下一个转换函数。
我们来看看转换函数。
- def assoc(_d, key, value):
- from copy import deepcopy
- d = deepcopy(_d)
- d[key] = value
- return d
-
- def set_canada_as_country(band):
- return assoc(band, 'country', "Canada")
-
- def strip_punctuation_from_name(band):
- return assoc(band, 'name', band['name'].replace('.', ''))
-
- def capitalize_names(band):
- return assoc(band, 'name', band['name'].title())
每个函数都将乐队的一个键与一个新值相关联。如果不变更原乐队,没有简单的方法可以直接实现。 assoc() 通过使用 deepcopy() 生成传入字典的副本来解决此问题。每个转换函数都对副本进行修改并返回该副本。
一切似乎都很好。当键与新值相关联时,可以保护原乐队字典免于被变更。但是上面的代码中还有另外两个潜在的变更。在 strip_punctuation_from_name() 中,原来的乐队名通过调用 replace() 生成无标点的乐队名。在 capitalize_names() 中,原来的乐队名通过调用 title() 生成大写乐队名。如果 replace() 和 title() 不是函数式的,则 strip_punctuation_from_name() 和 capitalize_names() 也将不是函数式的。
幸运的是, replace() 和 title() 不会变更他们操作的字符串。这是因为字符串在 Python 中是不可变的( immutable )。例如,当 replace() 对乐队名字符串进行操作时,将复制原来的乐队名并在副本上执行 replace() 调用。Phew~有惊无险!
Python 中字符串和字典之间在可变性上不同的这种对比彰显了像 Clojure 这样语言的吸引力。 Clojure 程序员完全不需要考虑是否会改变数据。 Clojure 的数据结构是不可变的。
练习4:尝试编写 pipeline_each 函数的实现。想想操作的顺序。数组中的乐队一次一个传递到第一个变换函数。然后返回的结果乐队数组中一次一个乐队传递给第二个变换函数。以此类推。
我的实现方案:
- def pipeline_each(data, fns):
- return reduce(lambda a, x: map(x, a),
- fns,
- data)
所有三个转换函数都可以归结为对传入的乐队的特定字段进行更改。可以用 call() 来抽象, call() 传入一个函数和键名,用键对应的值来调用这个函数。
- set_canada_as_country = call(lambda x: 'Canada', 'country')
- strip_punctuation_from_name = call(lambda x: x.replace('.', ''), 'name')
- capitalize_names = call(str.title, 'name')
-
- print pipeline_each(bands, [set_canada_as_country,
- strip_punctuation_from_name,
- capitalize_names])
(编辑:晋中站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|