python 食谱-day02
1.11 命名切片
record = '...............................100..........523.35'
#解法一、使用硬编码下标值
cost = int(record[31:34]) * float(record[44:50])
print(cost)
#解法二、命名切片
shares = slice(31,34)
price = slice(44,50)
print(int(record[shares])*float(record[price]))
第二种方式更加清晰可读、可维护性也高
内置的slice()
函数创建了一个切片对象,可以被用在任何切片允许使用的地方。
s = slice(3,50,2)
print(s.start)
print(s.stop)
print(s.step)
3
50
2
1.12序列中出现次数最多的元素
怎样找出一个序列中出现次数最多的元素呢?
collections.Counter
类就是专门为这类问题而设计的,甚至它提供了一个 most_common()
方法可以直接给你答案。
words = ['look','into','my','eyes','look','into','my','eyes','haha','hehe','hhuhu']
from collections import Counter
word_counts = Counter(words)
top_three = word_counts.most_common(3)#出现次数最多的3个单词
print(top_three)
[('into', 2), ('look', 2), ('eyes', 2)]
作为输入,Counter
对象可以接受任意的hashable
序列对象。在底层实现上,一个Counter
对象就是一个字典,将元素映射到它出现的次数上。
如果你想手动增加技术,可以简单的使用加法:
words = ['look','into','my','eyes','look','into','my','eyes','haha','hehe','hhuhu']
from collections import Counter
word_counts = Counter(words)
more_words = ['good','good','good','study']
word_counts.update(more_words)
top_three = word_counts.most_common(3)#出现次数最多的3个单词
print(word_counts)
Counter({'good': 3, 'into': 2, 'eyes': 2, 'my': 2, 'look': 2, 'haha': 1, 'study': 1, 'hehe': 1, 'hhuhu': 1})
Counter
实例可以很容易的跟数学运算操作相结合。所以,Counter
对象在几乎所有需要制表或者计数数据的场合是非常有用的工具。在解决这类问题的时候你应该优先选择它,而不是手动的利用字典去实现。
1.13通过某个关键字排序一个字典列表
你有一个字典列表,你想根据某个或几个字典字段来排序这个列表。
通过使用 operator
模块的 itemgetter
函数,可以非常容易的排序这样的数据结构。如下:
rows = [
{'fname':'Brian','lname':'Jones','uid':1003},
{'fname':'David','lname':'Beazley','uid':1002},
{'fname':'John','lname':'Cleese','uid':1001},
{'fname':'Big','lname':'Jones','uid':1004},
]
from operator import itemgetter
rows_by_fname = sorted(rows,key=itemgetter('fname'))
print(rows_by_fname)
rows_by_uid = sorted(rows,key=itemgetter('uid'))
print(rows_by_uid)
[{'lname': 'Jones', 'uid': 1004, 'fname': 'Big'}, {'lname': 'Jones', 'uid': 1003, 'fname': 'Brian'}, {'lname': 'Beazley', 'uid': 1002, 'fname': 'David'}, {'lname': 'Cleese', 'uid': 1001, 'fname': 'John'}]
[{'lname': 'Cleese', 'uid': 1001, 'fname': 'John'}, {'lname': 'Beazley', 'uid': 1002, 'fname': 'David'}, {'lname': 'Jones', 'uid': 1003, 'fname': 'Brian'}, {'lname': 'Jones', 'uid': 1004, 'fname': 'Big'}]
itemgetter()
函数也支持多个keys,比如
rows_by_lfname = sorted(rows,key=itemgetter('lname','fname'))
print(rows_by_lfname)
[{'uid': 1002, 'lname': 'Beazley', 'fname': 'David'}, {'uid': 1001, 'lname': 'Cleese', 'fname': 'John'}, {'uid': 1004, 'lname': 'Jones', 'fname': 'Big'}, {'uid': 1003, 'lname': 'Jones', 'fname': 'Brian'}]
这些东西同样使用于min()
和 max()
等函数
1.14排序不支持原生比较的对象
你想排序类型相同的对象,但是他们不支持原生的比较操作。
class User:
def __init__(self,user_id):
self.user_id = user_id
def __repr__(self):
return 'User({})'.format(self.user_id)
from operator import attrgetter
users = [User(12),User(33),User(24),User(35)]
a = sorted(users,key=attrgetter('user_id'))
print(a)
[User(12), User(24), User(33), User(35)]
也可以使用lambda
函数,
def sort_notcompare():
users = [User(12),User(23),User(34),User(45)]
print(sorted(users,key=lambda u:u.user_id))
sort_notcompare()
使用attrgetter()
函数通常会运行的快点,并且还能同事允许多个字段进行比较。同样的min()
和max()
适用于这些东西
1.15 通过某个字段将记录分组
你有一个字典或者实力的序列,然后你想根据某个特定的字段比如date
来分组迭代访问。
itertools.groupby()
函数对于这样的数据分组操作非常实用。
rows = [
{'address':'安徽淮北','date':'07/01/2012'},
{'address':'安徽蚌埠','date':'07/04/2012'},
{'address':'安徽淮南','date':'07/02/2012'},
{'address':'安徽黑肥','date':'07/03/2012'},
{'address':'安徽芜湖','date':'07/02/2012'},
{'address':'安徽黄山','date':'07/02/2012'},
{'address':'安徽六安','date':'07/01/2012'},
{'address':'安徽宿州','date':'07/04/2012'},
]
from operator import itemgetter
from itertools import groupby
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
{'date': '07/01/2012', 'address': '安徽淮北'}
{'date': '07/01/2012', 'address': '安徽六安'}
07/02/2012
{'date': '07/02/2012', 'address': '安徽淮南'}
{'date': '07/02/2012', 'address': '安徽芜湖'}
{'date': '07/02/2012', 'address': '安徽黄山'}
07/03/2012
{'date': '07/03/2012', 'address': '安徽黑肥'}
07/04/2012
{'date': '07/04/2012', 'address': '安徽蚌埠'}
{'date': '07/04/2012', 'address': '安徽宿州'}
1.16过滤序列元素
你有一个数据序列,想利用一些规则从中提取出需要的值或者是缩短序列
最简单的方法就是实用列表推导。
mylist = [1,4,-5,10,-7,2,3,-1]
[print(n) for n in mylist if n > 0]
print('++++++')
[print(n) for n in mylist if n < 0]
1
4
10
2
3
++++++
-5
-7
-1
使用列表推导的一个缺陷就是如果输入非常大的时候会产生一个非常大的结果集,占用大量内存。如果你对内存比较敏感,那么你可以使用生成器表达式迭代产生过滤的元素。
pos = (x for x in mylist if x < 0)
print(pos)
for x in pos:
print(x)
<generator object <genexpr> at 0x101bd0a40>
-5
-7
-1
当过滤规则比较复杂,不能简单的在列表推导或者生成器表达式中表达出来的时候,可以将过滤代码放到一个函数中,然后使用内置的filter()
函数。
values = ['1','2','-3','-','4','N/A','5']
def is_int(val):
try:
x = int(val)
return True
except ValueError:
return False
ivals = list(filter(is_int,values))
print(ivals)
['1', '2', '-3', '4', '5']
还有一个过滤工具是itertools.compress()
,它以一个iterable
对象和一个相对应的 Boolean
选择器序列作为输入参数。然后输出iterable
对象中对应选择器为True
的元素。当你需要用另一个相关联的序列来过滤某个序列的时候,这个函数是非常有用的。
addresses = [
'安徽淮北',
'安徽南京',
'安徽马鞍山',
'安徽芜湖',
'安徽北京',
'安徽合肥',
'安徽悉尼',
'内蒙古伦敦',
]
counts = [8,2,3,4,5,6,7,8];
from itertools import compress
neq8 = [n==8 for n in counts ]
print(list(compress(addresses,neq8)))
以上代码的作用是,将那些对应count
值大于5的地址全部输出。
这里的关键点在于先创建一个 Boolean
序列,提示哪些元素符合条件,然后compress()
函数根据这个序列去选择输出对应位置为True
的元素。
1.17从字典中提取子集
你想构建一个字典,它是另一个字典的子集。(过滤字典中符合条件的部分数据)
prices = {
'ACME':45.23,
'APPL':612.78,
'IBM':205.55,
'HPQ':37.20,
'FB':10.75
}
p1 = {key:value for key,value in prices.items() if value > 200}
print(p1)
tech_names = {'APPL','FB','MAOTAI','XIAOMI'}
p2 = {key:value for key,value in prices.items() if key in tech_names}
print(p2)
1.18映射名称到序列元素
通过名称来访问列表或者元组中的元素。
collections.namedtuple()
函数通过使用一个普通的元组对象来帮你解决这个问题。这个函数实际上是一个返回Python中标准元组类型子类的一个工厂方法。你需要传递一个类型名和你需要的字段给它,然后它就会返回一个类,你可以初始化这个类,为你定义的字段传递值等。
from collections import namedtuple
Subscriber = namedtuple('d',['one','two','three'])
sub = Subscriber('1','2','3')
print(sub)
print(sub.one)
print(sub.two)
d(one='1', two='2', three='3')
1
2
注:namedtuple
的实例跟元组类型是可交换的,支持所有的普通元组操作,比如索引和解压
print(len(sub))
x,y,h = sub
print(y)
3
2
命名元组的一个主要用途是将你的代码从下标操作中解脱出来。因此你从数据库调用中返回了一个很大的元素列表,通过下标去操作其中的元素,当你在表中添加了新的列的时候你的代码可能就出错了。但是如果你使用了命名元组,那么就不会有这样的顾虑。
例
from collections import namedtuple
Stock = namedtuple('Stock',['name','shares','price'])
def compute_cost(records):
total = 0.0
for rec in records:
#s = Stock(*rec)可以省略,后边直接使用rec.shares
total += s.shares * s.price
return total
recs = [Stock("红酒",1,2),Stock("红酒",2,2),Stock("红酒",3,2),Stock("红酒",4,2),Stock("红酒",5,2)]
print(compute_cost(recs))
30.0
命名元组另一个用于就是作为字典的替代
,因为字典存储需要更多的内存空间。但是命名元组是不可以改变
的.
s = Stock("coffee",2,5)
print(s)
print(s.name)
Stock(name='coffee', shares=2, price=5)
File "/Users/mudy/Documents/Python/Fishc/day01.py", line 475, in <module>
coffee
s.name = "冰红茶"
AttributeError: can't set attribute
如果真的需要改变其属性值,那么可以使用命名元组实例的_replace()
方法,它会创建一个全新的命名元组饼将对应的字段用新的值取代,例
s = s._replace(name="冰红茶")
print(s)
1.19转换并同时计算数据
你需要在数据序列上执行聚集函数(比如sum()
,min()
,max()
),但是首先你需要先转换或者过滤数据.
计算平方和
nums = [1,2,3,4,5]
s = sum(num * num for num in nums)
print(s)
55
import os
files = os.listdir()
if any(name.endswith('.py') for name in files):
print('这里有一个python文件')
else:
print('这里没有python文件')
print(files)
print(type(files))
s = ('ACME',50,123.45)
print(','.join(str(x) for x in s))
这里有一个python文件
['.idea', 'day01.py', 'decorator.py', 'net.py', 'singleton.py', 'test.db', 'tk1.py', 'tk2.py', 'tk_entry.py', 'tk_tag.py']
<class 'list'>
ACME,50,123.45
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)
# print(min_shares)
res = (s['shares'] for s in portfolio)
print(res)
print(min(res))
<generator object <genexpr> at 0x101bd0a40>
20
min_shares = min(portfolio,key=lambda s:s['shares'])
print(min_shares)
{'shares': 20, 'name': 'AOL'}
1.20合并多个字典或映射
现在有多个字典或者映射,你想将它们从逻辑上合并为一个单一的映射后来执行某些操作,比如查找值或者检查某些键是否存在。
假如你有两个字典a和b:现在你必须在两个字典中之行查找操作。就可以使用collections
模块中的ChainMap
类
a = {'x':1,'z':3}
b = {'y':2,'z':4}
from collections import ChainMap
c = ChainMap(b,a)
print(c)
print(c['z'])
ChainMap({'y': 2, 'z': 4}, {'x': 1, 'z': 3})
4
注:注意ChainMap()中参数的顺序
一个ChainMap
接受多个字典并将它们在逻辑上变为一个字典。然后,这些字典并不是真的合并在一起了,ChainMap
类只是在内部创建了一个容纳这些字典的列表并重新定义了一些常见的字典操作来遍历这个列表。大部分字典操作都是可以正常使用的。
print(len(c))
print(list(c.values()))
3
[4, 2, 1]
如果出现重复键,那么第一次出现的映射值会被返回。因此。。
对于字典的更新或删除操作总是影响的是列表中的第一个字典。