推导式
Python从Haskell语言中取得灵感,创建了一系列的Python推导式(comprehension)。
它可以在不丧失代码可读性的前提下创建一系列容器,是Python颇受好评的特性。
列表推导式
应用场景
有一个列表中存了一些人名和性别的元组数据项,我需要将性别为男性(male)的数据项提取至一个新的列表中。
如下所示,在没有学习列表推导式之前, 你可能会这样做:
oldList = [
("Tom", "Male"),
("Jack", "Male"),
("Mary", "Female"),
("Laura", "Female")
]
newList = []
for tpl in oldList:
if tpl[-1].lower() == "male":
newList.append(tpl)
print(newList)
# [['Tom', 'Male'], ['Jack', 'Male']]
如果有列表推导式的帮助,它就会简单许多,语法如下:
newList = [ 被添加/操作的数据项 for 迭代变量 in 可迭代对象 if 条件]
列表推导式操作如下所示:
oldList = [
("Tom", "Male"),
("Jack", "Male"),
("Mary", "Female"),
("Laura", "Female")
]
newList = [tpl for tpl in oldList if tpl[-1].lower() == "male"] # ❶
print(newList)
# [['Tom', 'Male'], ['Jack', 'Male']]
❶:当数据项的判断为True时将自动添加至newList中,不能指定else条件
快速创建列表
使用列表推导式可以快速的创建一个列表。
如下所示,推导出数据项从1-10的列表:
newLi = [i for i in range(1, 11)]
print(newLi)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
下面的示例中,我们可以快速的使用双层循环推导出了一副扑克牌的列表:
newLi = [ i + str(j) for i in ["❤", "♧", "♤", "♢"] for j in range(1, 14)]
newLi.append("bigKing")
newLi.append("smallKing")
print(newLi)
结果如下:
['❤1', '❤2', '❤3', '❤4', '❤5', '❤6', '❤7', '❤8', '❤9', '❤10', '❤11', '❤12', '❤13', '♧1', '♧2', '♧3', '♧4', '♧5', '♧6', '♧7', '♧8', '♧9', '♧10', '♧11', '♧12', '♧13', '♤1', '♤2', '♤3', '♤4', '♤5', '♤6', '♤7', '♤8', '♤9', '♤10', '♤11', '♤12', '♤13', '♢1', '♢2', '♢3', '♢4', '♢5', '♢6', '♢7', '♢8', '♢9', '♢10', '♢11', '♢12', '♢13', 'bigKing', 'smallKing']
元素的处理
数据项在添加至列表之前,可以为其进行一些操作。
比如在上面扑克牌的示例中,我们将int对象转换为了str对象,同理也可以做一些别的操作。
如下示例,我们将推导出一个含有26字母的列表:
newLi = [chr(i) for i in range(65, 91)]
print(newLi)
# ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
迭代变量
需要注意的是,迭代变量与条件成立后添加的数据项没有任何关系,如下所示,一行代码创建10个A:
newLi = ["A" for i in range(10)]
print(newLi)
# ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A']
对于这个题目,有一种更简便的解法:
newLi = ["A"] * 10
字典推导式
使用示例
字典推导式和列表推导式大体差不多,但是外部是用花括号包裹,并且要有:进行key和value的区分。
如下所示,快速的推导出标准ASCII码表的对照关系:
asciiDict = {i: chr(i) for i in range(0, 128)}
print(asciiDict)
结果如下:
{0: '\x00', 1: '\x01', 2: '\x02', 3: '\x03', 4: '\x04', 5: '\x05', 6: '\x06', 7: '\x07', 8: '\x08', 9: '\t', 10: '\n', 11: '\x0b', 12: '\x0c', 13: '\r', 14: '\x0e', 15: '\x0f', 16: '\x10', 17: '\x11', 18: '\x12', 19: '\x13', 20: '\x14', 21: '\x15', 22: '\x16', 23: '\x17', 24: '\x18', 25: '\x19', 26: '\x1a', 27: '\x1b', 28: '\x1c', 29: '\x1d', 30: '\x1e', 31: '\x1f', 32: ' ', 33: '!', 34: '"', 35: '#', 36: '$', 37: '%', 38: '&', 39: "'", 40: '(', 41: ')', 42: '*', 43: '+', 44: ',', 45: '-', 46: '.', 47: '/', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 58: ':', 59: ';', 60: '<', 61: '=', 62: '>', 63: '?', 64: '@', 65: 'A', 66: 'B', 67: 'C', 68: 'D', 69: 'E', 70: 'F', 71: 'G', 72: 'H', 73: 'I', 74: 'J', 75: 'K', 76: 'L', 77: 'M', 78: 'N', 79: 'O', 80: 'P', 81: 'Q', 82: 'R', 83: 'S', 84: 'T', 85: 'U', 86: 'V', 87: 'W', 88: 'X', 89: 'Y', 90: 'Z', 91: '[', 92: '\\', 93: ']', 94: '^', 95: '_', 96: '`', 97: 'a', 98: 'b', 99: 'c', 100: 'd', 101: 'e', 102: 'f', 103: 'g', 104: 'h', 105: 'i', 106: 'j', 107: 'k', 108: 'l', 109: 'm', 110: 'n', 111: 'o', 112: 'p', 113: 'q', 114: 'r', 115: 's', 116: 't', 117: 'u', 118: 'v', 119: 'w', 120: 'x', 121: 'y', 122: 'z', 123: '{', 124: '|', 125: '}', 126: '~', 127: '\x7f'}
第2个例子,对于迭代一些特殊格式的容器,可以直接生成出一个字典:
userMessage = [("name", "Yunya"), ("age", 18), ("gender", "male")]
newDict = {k: v for k, v in userMessage}
print(newDict)
# {'name': 'Yunya', 'age': 18, 'gender': 'male'}
集合推导式
使用示例
集合推导式只需要将列表推导式的[]改为{}即可。
以下是示例演示,很显然,由于是集合推导式,故下面的双层循环并不会产生重复元素:
newSet = {j for i in range(10) for j in range(10)}
print(newSet)
# {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
局部作用域
之前聊过普通的for循环中是不存在局部作用域的,那么在推导式当中是否存在局部作用域呢?
以列表举例:
newList = [i for i in range(10)]
print(i)
# NameError: name 'i' is not defined
可以看见,在推导式中的局部作用域是存在的。