函数式 编程则将一个问题分解成一系列函数。理想情况下,函数只接受输入并输出结果,对一个给定的输入也不会有影响输出的内部状态。著名的函数式语言有 ML 家族(Standard ML,Ocaml 以及其他变种)和 Haskell。
在函数式程序里,输入会流经一系列函数。每个函数接受输入并输出结果。函数式风格反对使用带有副作用的函数,这些副作用会修改内部状态,或者引起一些无法体现在函数的返回值中的变化。完全不产生副作用的函数被称作“纯函数”。消除副作用意味着不能使用随程序运行而更新的数据结构;每个函数的输出必须只依赖于输入。
真实案例
上面是一些基本的理论,在实际的工作当中,经常会看到从一个多层嵌套的字典当中取一个值,会使用连续的下标,但是如果中间某个值并非一个字典,就会导致程序的运行时错误。而这种错误经常得等到代码上线之后才会被发现,开发的时候经常都是测试的正常数据。
例如:# 查询Elasticsearch,获得检索结果
resp = es_request("_search", dsl)
# 从检索结果当中取出结果总数
total = resp["hits"]["total"]["value"]
上面的代码通常不会有问题,因为Elasticsearch的检索结果总是会包含 hits/total/value 这个值,如果没有结果,这个值就为0。但是,看看下面的动态取值:value = resp["data"]["aggs"][aggs_key]["value"]
这是在封装了Elasticsearch查询之后的结果,假设是一个叫search_service的微服务,如果aggs下的值为空,那么再取动态的 aggs_key 的值就会报 Key Error 的错误。
这是在我们产品当中真实发现的情况。下面介绍一个我自己写的,对字典、列表、嵌套字典与列表的取值的通用方法 get_in ,名字来源于 Clojure
的一个内置方法 get-in 。
如果是取一个字典的值,同时又不想得到Key Error的错误,Python提供的一个便捷的方法:# 定义一个字典对象
d = {'a': 1, 'b': 2}
# 从字典当中取出键为 a 的值,如果键 a 不存在,返回 default value
v = d.get('a', 'default value')
字典对象在Python当中有一个get方法,还可以传一个默认值,这样就不会报错了。来看看这个方法的定义与文档get(key[, default])
Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.
下面正式介绍我们的get_in函数,来看函数定义:from typing import List, Union, Dict
def get_in(data: Union[Dict, List], ks: List, default={}):
"""根据ks指定的键或者索引从字典或者列表当中取值.
Args:
data (dict|list): 字典或者数组,或者两者的嵌套
ks (list): 取值路径
default (Any): 当没有ks对应的路径是的默认值
Examples:
>>> get_in({'a': 1}, ['a'])
1
>>> get_in({'a': {'b': 2}}, ['a', 'b'])
2
>>> get_in({'a': [1, 2, 3]}, ['a', 1])
2
>>> get_in({'a': {'b': 2}}, ['a', 'c'], default=9)
9
Returns:
Any: 数组或者字典的值
"""
cursor = data
for k in ks:
if isinstance(k, int) and isinstance(cursor, list):
if len(cursor) == 0:
return default
cursor = cursor[k]
elif isinstance(cursor, dict):
cursor = cursor.get(k, None)
else:
return default
if cursor is None:
return default
return cursor
data = {'a': 1, 'b': 2}
ks = ['a']
v = get_in(data, ks, default=-1)
data = [1, 2, 3, 4, 5]
ks = [1]
v = get_in(data, ks, default=-1)
data = {'a': [1, 2, 3], 'b': {'c': 9}}
ks = ['a', 1]
v = get_in(data, ks, default=-1)
这个数据稍微复杂一点了,字典data的健a对应的是一个列表了。ks表示取键a对应的值下面列表的下标为 1 的值,默认返回-1。最后 v 的结果是 2data = {'a': {'b': {'c': 9}}}
ks = ['a', 'b', 'c']
v = get_in(data, ks, default=-1)
这样就可以取到 9 这个值了data = {'a': {'b': {'c': 9}}}
ks = ['a', 'z', 'c']
v = get_in(data, ks, default=-1)
v 的值为 -1 ,同时还不会有 Key Error的错误
get_in 函数还可以处理非常复杂,嵌套得非常深的字典与列表的数据。还可以对里面的判断做一个修改,添加自定义类型的支持,对这个函数进行扩展。
结论
Python是一门多范式的编程语言,通过灵活的语言特性可以得到非常灵活、优雅、健壮的代码。函数式编程是其中一个重要的范式,可以将一个问题分解成一系列函数,每个函数接受输入并输出结果。函数式风格反对使用带有副作用的函数,消除副作用意味着不能使用随程序运行而更新的数据结构;每个函数的输出必须只依赖于输入。本篇文章介绍了一个函数式编程的案例——Python函数式编程之get_in,从一个多层嵌套的字典或列表中取一个值。通过使用函数式编程的方式,可以编写出通用的、可以处理非常复杂、嵌套得非常深的字典与列表的数据的代码。
封面图使用AI生成
Scan to Follow