Python Cookbook学习笔记ch1_03

1.15通过某个字段将记录分组

  • 问题:如何根据某个特定的字段对一个序列分组迭代访问
  • 方案:使用itertools.groupby().使用groupby时首先要对元素进行排序。因为groupby只会扫描连续的元素
rows = [
{'address': '5412 N CLARK', 'date': '07/01/2012'},
{'address': '5148 N CLARK', 'date': '07/04/2012'},
{'address': '5800 E 58TH', 'date': '07/02/2012'},
{'address': '2122 N CLARK', 'date': '07/03/2012'},
{'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
{'address': '1060 W ADDISON', 'date': '07/02/2012'},
{'address': '4801 N BROADWAY', 'date': '07/01/2012'},
{'address': '1039 W GRANVILLE', 'date': '07/04/2012'},
]
from itertools import groupby
from operator import itemgetter
rows.sort(key=itemgetter('date'))
for date,items in groupby(rows,key=itemgetter('date')):
    print(date)
    for i in items:
        print(' ',i)
07/01/2012
  {'address': '5412 N CLARK', 'date': '07/01/2012'}
  {'address': '4801 N BROADWAY', 'date': '07/01/2012'}
07/02/2012
  {'address': '5800 E 58TH', 'date': '07/02/2012'}
  {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'}
  {'address': '1060 W ADDISON', 'date': '07/02/2012'}
07/03/2012
  {'address': '2122 N CLARK', 'date': '07/03/2012'}
07/04/2012
  {'address': '5148 N CLARK', 'date': '07/04/2012'}
  {'address': '1039 W GRANVILLE', 'date': '07/04/2012'}
from collections import defaultdict
row_by_date = defaultdict(list)
for row in rows:
    row_by_date[row['date']].append(row)
for r in row_by_date['07/01/2012']:
    print (r)
{'address': '5412 N CLARK', 'date': '07/01/2012'}
{'address': '4801 N BROADWAY', 'date': '07/01/2012'}
row_by_date
defaultdict(list,
            {'07/01/2012': [{'address': '5412 N CLARK', 'date': '07/01/2012'},
              {'address': '4801 N BROADWAY', 'date': '07/01/2012'}],
             '07/02/2012': [{'address': '5800 E 58TH', 'date': '07/02/2012'},
              {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
              {'address': '1060 W ADDISON', 'date': '07/02/2012'}],
             '07/03/2012': [{'address': '2122 N CLARK', 'date': '07/03/2012'}],
             '07/04/2012': [{'address': '5148 N CLARK', 'date': '07/04/2012'},
              {'address': '1039 W GRANVILLE', 'date': '07/04/2012'}]})

1.16过滤序列元素

  • 问题:有一个序列,想利用一些规则提取出需要的值或者缩短序列
  • 方案:最近简单的是使用列表推导
  • 缺陷:会产生一个较大的结果集,占用内存
my_list = [1,4,2,-4,8,0,2,-11,20]
[n for n in my_list if n>0]
[1, 4, 2, 8, 2, 20]
[n for n in my_list if n<0]
[-4, -11]
[2*n for n in my_list if n>0]
[2, 8, 4, 16, 4, 40]
  • 可以使用生成器表达式迭代产生过滤的元素
gen = (n for n in my_list if n>0)
gen
<generator object <genexpr> at 0x00A14810>
for n in gen:
    print(n,end=' ')
1 4 2 8 2 20 
  • 但是有时候过滤规则比较复杂,不能简单地在列表推导或者生成器表达式中表达。此时可以将过滤代码放到一个函数中,然后使用内建函数filter()
values = ['1','2','-3','-','4','N/A','5']
def is_int(val):
    try:
        x = int(val)
        return True
    except ValueError:
        return False

list(filter(is_int,values))
['1', '2', '-3', '4', '5']
  • 过滤操作的一个变种就是将不符合条件的值用新的值代替,而不是丢弃它们。比如,在一列数据中你可能不仅想找到正数,而且还想将不是正数的数替换成指定的数。通过将过滤条件放到条件表达式中去
values = [1,2,3,-1,-4,0,-4,6,11,-2,112]
[n if n>0 else 0 for n in values]
[1, 2, 3, 0, 0, 0, 0, 6, 11, 0, 112]
  • 另一个过滤工具是使用itertools.compress(),它以一个 iterable对象和一个相对应的 Boolean 选择器序列作为输入参数。然后输出 iterable 对象中对应选择器为 True 的元素。
addresses = [
'5412 N CLARK',
'5148 N CLARK',
'5800 E 58TH',
'2122 N CLARK',
'5645 N RAVENSWOOD',
'1060 W ADDISON',
'4801 N BROADWAY',
'1039 W GRANVILLE',
]
counts = [0,3,10,4,1,7,6,1]
from itertools import compress
r = compress(addresses,[n>5 for n in counts])
r
<itertools.compress at 0xa253b0>
list(r)
['5800 E 58TH', '1060 W ADDISON', '4801 N BROADWAY']
[n>5 for n in counts]
[False, False, True, False, False, True, True, False]

1.17从字典中提取子集

  • 问题:想构造一个字典,它是另一个字典的子集
  • 方案:最简单的是使用字典推导
prices = {
'ACME': 45.23,
'AAPL': 612.78,
'IBM': 205.55,
'HPQ': 37.20,
'FB': 10.75
}
#我的方案
new_price = {key:prices[key] for key in prices if prices[key]>200}
new_price
{'AAPL': 612.78, 'IBM': 205.55}
#书上的方案
new_price = {key:value for key,value in prices.items() if value > 200}
new_price
{'AAPL': 612.78, 'IBM': 205.55}
prices.items()
dict_items([('ACME', 45.23), ('AAPL', 612.78), ('IBM', 205.55), ('HPQ', 37.2), ('FB', 10.75)])
  • 多数情况,字典推导可以完成的,通过创建一个元祖序列然后传入dict()也可以实现.但是此方法慢
dict((key,value) for key,value in prices.items() if value > 200)
{'AAPL': 612.78, 'IBM': 205.55}
tech_names = { 'AAPL', 'IBM', 'HPQ', 'MSFT' }
p = {key:prices[key] for key in prices.keys() & tech_names}
p
{'IBM': 205.55, 'HPQ': 37.2, 'AAPL': 612.78}

1.18映射名称导序列元素

  • 问题:你有一段通过下标访问列表或者元组中元素的代码,但是这样有时候会使得你的代码难以阅读,于是你想通过名称来访问元素
  • 方案:collections.namedtuple() 函数通过使用一个普通的元组对象来帮你解决这个问题。这个函数实际上是一个返回 Python 中标准元组类型子类的一个工厂方法。你需要传递一个类型名和你需要的字段给它,然后它就会返回一个类,你可以初始化这个类,为你定义的字段传递值等。
from collections import namedtuple
Subscriber = namedtuple('Subscriber',['addr','joined'])
sub = Subscriber('jonesy@example.com','2012-10-19')
sub
Subscriber(addr='jonesy@example.com', joined='2012-10-19')
sub.addr
'jonesy@example.com'
sub.joined
'2012-10-19'
len(sub)
2
addr,joined = sub
addr
'jonesy@example.com'
  • 命名元组的一个主要用途是将你的代码从下标操作中解脱出来。因此,如果你从数据库调用中返回了一个很大的元组列表,通过下标去操作其中的元素,当你在表中添加了新的列的时候你的代码可能就会出错了。但是如果你使用了命名元组,那么就不会有这样的顾虑。
# 普通代码,假设记录的第2列是单价,第3列是数量
def compute_cost(records):
    total = 0.0
    for rec in records:
        total += rec[1]*rec[2]
    return total
# 使用命名元组,假设记录的第2列是单价,第3列是数量
stock = namedtuple('Stock',['name','price','shares'])
def compute_cost(records):
    total = 0.0
    for rec in records:
        s = stock(*rec)
        total += s.shares * s.price
    return total
  • 命名元组另一个用途就是作为字典的替代,因为字典存储需要更多的内存空间。如果你需要构建一个非常大的包含字典的数据结构,那么使用命名元组会更加高效。但是需要注意的是,不像字典那样,一个命名元组是不可更改的。
s = stock('ACME',100.23,1000)
s
Stock(name='ACME', price=100.23, shares=1000)
s.shares = 10
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-80-d95f3d63f5c1> in <module>()
----> 1 s.shares = 10


AttributeError: can't set attribute
  • 如果你真的需要改变属性的值,那么可以使用命名元组实例的 _replace() 方法,它会创建一个全新的命名元组并将对应的字段用新的值取代。
s._replace(shares = 10)
Stock(name='ACME', price=100.23, shares=10)
  • _replace() 方法还有一个很有用的特性就是当你的命名元组拥有可选或者缺失字段时候,它是一个非常方便的填充数据的方法。你可以先创建一个包含缺省值的原型元组,然后使用 _replace() 方法创建新的值被更新过的实例。
from collections import namedtuple
Stock = namedtuple('Stock', ['name', 'shares', 'price', 'date', 'time'])
stock_prototype = Stock('',0,0.0,None,None)
def dict_to_stock(s):
    return stock_prototype._replace(**s)
a = {'name':'ACME','shares':100,'price':123.44}
dict_to_stock(a)
Stock(name='ACME', shares=100, price=123.44, date=None, time=None)
b = {'name': 'ACME', 'shares': 100, 'price': 123.45, 'date': '12/17/2012'}
dict_to_stock(b)
Stock(name='ACME', shares=100, price=123.45, date='12/17/2012', time=None)

1.18转换并同时计算数据

  • 问题:在数据上执行聚集函数(sum,min,max),但首先需要转换或者过滤数据
  • 方案:使用生成器表达式参数
  • 优点:使用迭代的方式,节省内存
nums= [1,2,3,4,5]
s = sum(x*x for x in nums)
#等效于s = sum((x*x for x in nums))
s
55
  • 下面的方法可以实现相同的效果,但是不属于生成器表达式,它会先生成一个list,然后计算,会找用很多内存
sum([x*x for x in nums])
55
# 确定某个文件下是否存在.py文件
import os
files = os.listdir('F:/ML')
if any(name.endswith('.py') for name in files):
    print('Yes,exist python file')
else:
    print('sorry,no python file')

s = ('ACME',50,123.33)
print(','.join(str(x) for x in s))

portfolio = [
{'name':'GOOG', 'shares': 50},
{'name':'YHOO', 'shares': 75},
{'name':'AOL', 'shares': 20},
{'name':'SCOX', 'shares': 65}
]
min_shares = min(s['shares'] for s in portfolio)
min_shares
Yes,exist python file
ACME,50,123.33





20

1.19合并多个字典或者映射

  • 问题:现在有多个字典或者映射,将其合并为一个并可以执行某些操作
  • 方案:可以使用collections模块中的ChainMap类
a = {'x':1,'z':3}
b = { 'y':2,'z':4}
from collections import ChainMap
c = ChainMap(a,b)
print(c['x'])
print(c['y'])
print(c['z'])
1
2
3
  • 一个 ChainMap 接受多个字典并将它们在逻辑上变为一个字典。然后,这些字典并不是真的合并在一起了, ChainMap 类只是在内部创建了一个容纳这些字典的列表并重新定义了一些常见的字典操作来遍历这个列表。大部分字典操作都是可以正常使用的
len(c)
3
list(c.keys())
['y', 'x', 'z']
list(c.values())
[2, 1, 3]
  • 如果出现重复键,那么第一次出现的映射值会被返回。因此,例子程序中的 c[‘z’]总是会返回字典 a 中对应的值,而不是 b 中对应的值。对于字典的更新或删除操作总是影响的是列表中第一个字典。
c['z'] = 10
c['w'] = 20
del c['x']
c
ChainMap({'z': 10, 'w': 20}, {'y': 2, 'z': 4})
a
{'z': 10, 'w': 20}
del c['y']
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

d:\program filles\python\lib\collections\__init__.py in __delitem__(self, key)
    933         try:
--> 934             del self.maps[0][key]
    935         except KeyError:


KeyError: 'y'


During handling of the above exception, another exception occurred:


KeyError                                  Traceback (most recent call last)

<ipython-input-122-1b93a26af00d> in <module>()
----> 1 del c['y']


d:\program filles\python\lib\collections\__init__.py in __delitem__(self, key)
    934             del self.maps[0][key]
    935         except KeyError:
--> 936             raise KeyError('Key not found in the first mapping: {!r}'.format(key))
    937 
    938     def popitem(self):


KeyError: "Key not found in the first mapping: 'y'"
  • ChainMap对于编程语言中的作用范围变量很有用
values = ChainMap()
values['x'] = 1
values = values.new_child()
values['x'] = 2
values = values.new_child()
values['x'] = 3
values
ChainMap({'x': 3}, {'x': 2}, {'x': 1})
values['x']
3
#discarding last mapping
values = values.parents
values['x']
2
values
ChainMap({'x': 2}, {'x': 1})
  • 作为ChainMap的替代,可以使用update()方法将两个字典合并.但是它需要你创建一个完全不同的字典对象(或者是破坏现有
    字典结构)。同时,如果原字典做了更新,这种改变不会反应到新的合并字典中去
a = {'x':1,'z':3}
b = { 'y':2,'z':4}
merged = b
merged.update(a)
merged
{'y': 2, 'z': 3, 'x': 1}
merged['x']
1
a['x'] = 100
merged['x']
1
  • 但是使用ChainMap会影响到合并后的字典
a = {'x':1,'z':3}
b = { 'y':2,'z':4}
merged = ChainMap(a,b)
a['x'] = 100
merged['x']
100
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值