一、元组或序列的小技巧
1.1 序列分解
list1 = [2, 'apple', (1,2,3), [4,4]]
num, *mid, _ = list1
print(num)
2
print(mid)
['apple', (1, 2, 3)]
从上面的代码输出可以看到:
num 被赋予了第1个位置的值;- 得益于
* 这个符号,mid 分别被赋予了从第2个位置开始到倒数第1个之前的值; _ 表示丢弃当前位置的值。,
当然这也适用于字符串(String)与元组(Tuple),例如:
string1 = 'Hello world!'
first, *_, final = string1
print(first)
H
print(final)
!
上述的代码中需要注意的,* 这个符号,可以与_ 搭配使用,其将中间的位置值全部丢弃了。
1.2 序列去重
如果只是简单地去除重复项,可以使用set ,如下:
a = [1, 5, 2, 1, 2, 3]
set(a)
{1, 2, 3, 5}
但上述的方法存在缺陷,不能保证原本的元素间顺序不变。
因此我们可以这么解决,如果序列中的值是可哈希的(hashable),如下:
如果一个对象是可哈希的,那么它是不可变的,需要有一个__hash__() 方法,例如整数、浮点数、字符串、元组是不可变的。
def dedupe(items):
seen = set()
for item in items:
if item not in seen:
yield item
seen.add(item)
a = [1, 5, 2, 1, 2, 3]
print(list(dedupe(a)))
[1, 5, 2, 3]
那么如果序列中的元素不可哈希的,该如何做呢?代码如下:
def dedupe(items, key):
seen = set()
for item in items:
val = item if key is None else key(item)
if val not in seen:
yield item
seen.add(val)
a = [{'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]
print(list(dedupe(a, key=lambda d: (d['x'], d['y']))))
[{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]
1.3 序列中次数最多元素
可以使用collection 包来解决找到序列中出现次数最多的元素,如下:
words = ['a', 'dog', 'a', 'people', 'like', 'see', 'you', 'people', 'dog', 'a']
from collections import Counter
word_count = Counter(words)
top_three = word_count.most_common(3)
print(top_three)
[('a', 3), ('dog', 2), ('people', 2)]
在上面的基础上,可以调用update() 方法增加新的words,如下:
more_words = ['like', 'see', 'you']
word_count.update(more_words)
当然,可以与数学运算符号结合使用,如下:
a = Counter(words)
b = Counter(more_words)
print(a + b)
Counter({'a': 3, 'dog': 2, 'people': 2, 'like': 2, 'see': 2, 'you': 2})
print(a - b)
Counter({'a': 3, 'dog': 2, 'people': 2})
1.4 序列中筛选元素
筛选序列中的元素,最简单的办法是列表推导式(list comprehension) ,如下:
list1 = [1, 3, 4, -5, 0, 8, -3]
[n for n in list1 if n > 0]
[1, 3, 4, 8]
(n for n in list1 if n < 0)
<generator object <genexpr> at 0x000002575B79DC10>
for i in (n for n in list1 if n < 0):
print(i)
-5
-3
甚至可以有更复杂的情形,如下:
list1 = [1, 3, 4, -5, 0, 8, -3]
import math
[math.sqrt(n) if n > 0 else 0 for n in list1]
[1.0, 1.7320508075688772, 2.0, 0, 0, 2.8284271247461903, 0]
当然还可以使用内建的filter() 函数进行处理,(由于filter() 方法生成的是一个迭代器,因此我们需要添加list() 使其成为一个可视化的列表类型),如下:
list1 = ['1', '-2', '-', 'N/A']
def is_int(val):
try:
x = int(val)
return True
except ValueError:
return False
ivals = list(filter(is_int, list1))
print(ivals)
['1', '-2']
1.5 序列数据转换与换算
首先,优雅地对一个序列求平方和,如下:
list1 = [1, 5, 2, 1, 2, 3]
sum(x * x for x in list1)
44
当然可以更高级地,我们以字典列表为例子,如下:
prices = [
{'name':'banana', 'price':15, 'num': 3},
{'name':'apple', 'price':10, 'num': 2},
]
print(sum(s['price'] * s['num'] for s in prices))
65
二、字典的小技巧
2.1 字典的最值与排序
求一个字典中的最大值与最小值,一般会使用zip() 将字典中的key 与value 反转过来,如下:
prices = {
'banana': 16,
'apple': 11,
'meat': 30,
'fish': 23
}
min_price = min(zip(prices.values(), prices.keys()))
min_price
(11, 'apple')
max_price = max(zip(prices.values(), prices.keys()))
max_price
(30, 'meat')
若存在条目拥有相同的value 值,那么在比较大小时,将对key 值进行比较,如下:
prices = {
'banana': 11,
'apple': 11,
}
min(zip(prices.values(), prices.keys()))
(11, 'apple')
max(zip(prices.values(), prices.keys()))
(11, 'banana')
当然一般任务中,我们只需要知道最小values 值的key 是什么,如下:
prices = {
'banana': 16,
'apple': 11,
'meat': 30,
'fish': 23
}
min(prices, key=lambda k: prices[k])
'apple'
max(prices, key=lambda k: prices[k])
'meat'
下面是排序,排序要配合使用zip() 与sorted() ,如下:
price_sorted = sorted(zip(prices.values(), prices.keys()))
price_sorted
[(11, 'apple'), (16, 'banana'), (23, 'fish'), (30, 'meat')]
但需要注意,zip 创建的迭代器,内容只会被消费一次,例如下面就会报错:
price_temp = zip(prices.values(), prices.keys())
print(min(price_temp))
(11, 'apple')
print(max(price_temp))
ValueError: max() arg is an empty sequence
因为zip 中的内容在第一次调用的时候就被消耗掉了。
2.2 字典间的相同点
可以使用& 来寻找字典中的相同点,使用- 来寻找不同点,我们先设计两个字典如下:
a = {
'x': 11,
'y': 22,
'z': 33,
}
b = {
'v': 1,
'x': 2,
'y': 22,
}
寻找a 字典与b 字典中相同的keys ,如下:
print(a.keys() & b.keys())
{'y', 'x'}
寻找b 字典中不存在,但a 字典中存在的keys ,如下:
print(a.keys() - b.keys())
{'z'}
寻找a 字典与b 字典中相同的items ,如下:
print(a.items() & b.items())
{('y', 22)}
2.3 通过公共键对字典列表排序
rows = [
{'name':'wang', 'age':15},
{'name':'ye', 'age':19},
{'name':'dong', 'age':16},
{'name':'mao', 'age':14}
]
rows_by_age = sorted(rows, key=lambda r: r['age'])
print(rows_by_age)
[{'name': 'mao', 'age': 14},
{'name': 'wang', 'age': 15},
{'name': 'dong', 'age': 16},
{'name': 'ye', 'age': 19}]
另外的,可以通过调用operator 包下的itemgetter 方法来使用,如下:
from operator import itemgetter
rows_by_age = sorted(rows, key=itemgetter('age'))
print(rows_by_age)
[{'name': 'mao', 'age': 14},
{'name': 'wang', 'age': 15},
{'name': 'dong', 'age': 16},
{'name': 'ye', 'age': 19}]
同样的,上述方法可以作用于min() 、max() 这类函数,如下:
min(rows, key=itemgetter('age'))
{'name': 'mao', 'age': 14}
可以看到,lambad 表达式是一个十分有效的方法,在对于不原生支持排序的对象比较时候,应调用operator.attrgetter() 方法,不但可以对单变量排序,也可以同时选择多变量进行排序。
2.4 根据字段将记录分组
可以先按某关键词对该序列进行排序,然后通过调用itertools.groupby() 方法进行分组,如下:
rows = [
{'name':'wang', 'age':14},
{'name':'ye', 'age':19},
{'name':'dong', 'age':16},
{'name':'mao', 'age':14},
{'name':'hu', 'age': 16}
]
from operator import itemgetter
from itertools import groupby
rows.sort(key=itemgetter('age'))
for age, items in groupby(rows, key=itemgetter('age')):
print(age)
for i in items:
print(' ', i)
14
{'name': 'wang', 'age': 14}
{'name': 'mao', 'age': 14}
16
{'name': 'dong', 'age': 16}
{'name': 'hu', 'age': 16}
19
{'name': 'ye', 'age': 19}
2.5 字典提取子集
我们想创建一个小字典,其本身是另一个字典的子集,如下:
prices = {
'banana': 16,
'apple': 11,
'meat': 30,
'fish': 23
}
{key:value for key, value in prices.items() if value > 20}
{'meat': 30, 'fish': 23}
tmp_name = ['meat', 'apple']
{key:value for key, value in prices.items() if key in tmp_name}
{'apple': 11, 'meat': 30}
2.6 字典的替代 # 命名元组namedtuple
相对于字典,使用namedtuple 在大型数据结构中会更加高效,基础使用如下:
from collections import namedtuple
FRUIT = namedtuple('Fruit', ('name', 'price'))
sub = FRUIT('banana', 16)
print(sub.name, sub.price)
banana 16
也可以像元祖元组一样去得到里面的值,如下:
name, price = sub
print(name, price)
banana 16
如果要对其中的值进行改变,可以使用namedtuple实例 的_replace() 方法来实现,如下:
from collections import namedtuple
FRUIT = namedtuple('Fruit', ('name', 'price'))
sub = FRUIT('banana', 16)
sub._replace(price=20)
Fruit(name='banana', price=20)
_replace() 方法还有另一个用途,可以用来填充缺失数据的命名元组,需要先建立一个包含默认值的“原型”元组,然后再用该方法,如下:
from collections import namedtuple
FRUIT = namedtuple('Fruit', ('name', 'price'))
f_prototype = FRUIT('', 0)
def dict_to_FRUIT(x):
return f_prototype._replace(**x)
a = {'name': 'meat', 'price': 30}
dict_to_FRUIT(a)
Fruit(name='meat', price=30)
里面有一个操作,假设a = {'name': 'meat', 'price': 30} 是一个字典,则**a 表示name='meat', price=30 。
2.7 字典的合并
最基本的方法,可以使用dict 类型的update() 方法来合并两个字典,如下:
a = {'x': 1, 'z': 3}
b = {'y': 2, 'z': 4}
merged = b
merged.update(a)
print(merged['x'])
print(merged['y'])
2
print(merged['z'])
3
a['x'] = 13
merged['x']
1
可以观察到新建立的字典为merged ,但是如果改变字典a 中的x 的值并不会影响字典merged 中a 的值。
另一种方法是调用collection.ChainMap 类来构建,如下:
a = {'x': 1, 'z': 3}
b = {'y': 2, 'z': 4}
from collections import ChainMap
merged = ChainMap(a, b)
print(merged['z'])
3
a['x'] = 13
merged['x']
13
需要注意,Chainmap 对两个字典进行合并,若其中存在相同的关键词,则按照顺序进行对关键词进行赋值,如merged['z']==a['z'] ; Chainmap 对两个原始字典中的值发生变化,将会影响最终的merged 中的值变化。
三、切片的小技巧
切片(slice)可以命名!如下:
record = 'apple....10....5'
NAME = slice(0, 5)
PRICE = slice(9,11)
NUM = slice(15,16)
print(record[NAME])
apple
print(record[PRICE])
10
print(record[NUM])
5
slice 对象可以通过s.start 、s.stop 、s.step 查看属性,如下:
a = slice(5, 50, 2)
print(a.start)
5
print(a.stop)
50
print(a.step)
2
也可以调用indices(size) 的方法自动地调整已生成deed切片,使其符合特定size ,如下:
a = slice(1,20)
a.indices(10)
(1, 10, 1)
总结
查漏补缺~
参考:《Python cookbook 中文版》[美]David Beazley&Brian K. Jones 著
|