問題一:如何在列表,字典,集合中根據條件篩選數據?
問題內容:
如何找出列表中的負數?
如何篩選出字典中值大于某個數值的項?
如何篩選出集合中能夠被3整除的元素?
解決方案:
對于列表可以使用filter函數和列表解析:
>>> from random import randint
>>> data = [ randint(-10,10) for _ in range(10)]
>>> data
[-4, 0, 8, -2, -5, -9, 6, 5, 6, 6]
>>> filter(lambda x: x >=0,data)
<filter object at 0x7f51e28c0c18>
我們看到返回的不是一個列表,這是python3對filter函數的修改,需要使用list()函數轉換。
>>> list(filter(lambda x: x >=0,data))
[0, 8, 6, 5, 6, 6]
>>> [ x for x in data if x>= 0]
[0, 8, 6, 5, 6, 6]
上面的兩個方法中,列表解析耗費的時間要比filter()函數的少。
對于字典使用字典解析:
生成一個字典
>>> d = { x: randint(60,100) for x in range(1,21)}
>>> d
{1: 97, 2: 100, 3: 62, 4: 66, 5: 87, 6: 89, 7: 66, 8: 79, 9: 96, 10: 76, 11: 81, 12: 61, 13: 100, 14: 90, 15: 94, 16: 74,
17: 80, 18: 76, 19: 81, 20: 82}
查詢出所有值大于90的
>>> {k:v for k,v in d.items() if v>90}
{1: 97, 2: 100, 15: 94, 13: 100, 9: 96}
對于集合使用集合解析:
>>> s = set(data)
>>> { x for x in s if x % 3 ==0}
{0, 6, -9}
問題二:如何為元組中的每個元素命名,提高程序的可讀性?
問題內容:
學生信息系統中數據為固定格式:
(名字,年齡,性別,郵箱地址)
學生數量很大為了減小存儲開銷,對每個學生信息用元組表示:
('Wex',24,'female','wex@qq.com')
('Fangyw',23,female,'fangyw@163.com')
('Pandz',25,male,'pandz@qq.com')
......
訪問時,我們使用索引(index)訪問,大量索引降低程序可讀性,如何解決這個問題?
解決方案:
方案一:定義類似與其它語言的枚舉類型,也就是定義一系列數值常量。
方案二:使用標準庫中collections.namedtuple替代內置tuple
先看使用枚舉:
>>> NAME = 0
>>> AGE = 1
>>> SEX = 2
>>> EMAIL = 3
>>> student = ('Wex',24,'female','wex@qq.com')
>>> student[NAME]
'Wex'
變量的賦值也可以使用列表解包
>>> NAME,AGE,SEX,EMAIL=range(4)
>>> student[NAME]
'Wex'
我們接著看使用namedtuple:
首先我們看下namedtuple函數的用法:
┌──────────────────────────────────────────────────────────────────────────────────────────────┐
│ namedtuple: (typename, field_names, verbose=False, rename=False) │
│ Returns a new subclass of tuple with named fields. │
│ │
│ >>> Point = namedtuple('Point', ['x', 'y']) │
│ >>> Point.__doc__ # docstring for the new class │
│ 'Point(x, y)' │
│ >>> p = Point(11, y=22) # instantiate with positional args or keywords │
│ >>> p[0] + p[1] # indexable like a plain tuple │
│ 33 │
│ >>> x, y = p # unpack like a regular tuple │
│ >>> x, y │
│ (11, 22) │
│ >>> p.x + p.y # fields also accessible by name │
│ 33 │
│ >>> d = p._asdict() # convert to a dictionary │
│ >>> d['x'] │
│ 11 │
│ >>> Point(**d) # convert from a dictionary │
│ Point(x=11, y=22) │
│ >>> p._replace(x=100) # _replace() is like str.replace() but targets named fie │
│ lds │
│ Point(x=100, y=22) │
└──────────────────────────────────────────────────────────────────────────────────────────────┘
該函數會返回一個內置tuple類的子類。第一個參數是子類的名字,第二個參數是索引的名字列表。
>>> from collections import namedtuple
>>> namedtuple('Student',['name','age','sex','email'])
<class '__console__.Student'>
>>> Student = namedtuple('Student',['name','age','sex','email'])
可以直接使用類創建元組
>>> s1 = Student('Wex',24,'female','wex@qq.com')
>>> s1
Student(name='Wex', age=24, sex='female', email='wex@qq.com')
也可以按照屬性名創建
>>> s2 = Student(name='Fyw', age=22, sex='female', email='fyw@qq.com')
>>> s2
Student(name='Fyw', age=22, sex='female', email='fyw@qq.com')
直接通過屬性獲得值
>>> s1.name
'Wex'
>>> s2.age
22
返回的是一個tuple的子類
>>> type(s1)
<class '__console__.Student'>
>>> isinstance(s1,tuple)
True
問題三:如何統計列中元素的出現頻度?
問題內容:
1,隨機序列[12,1,2,3,4,5,4,3,4,5,...]中,找到出現次數最高的3個元素,它的出現次數是多少?
2,對某英文文章的單詞進行詞頻統計,找到出現次數最高的10個單詞,它們出現的次數是多少?
對于隨機序列:
使用遍歷的方法:
>>> from random import randint
創建一段隨機序列
>>> data = [randint(0,20) for _ in range(0,30)]
根據隨機序列的值為字典的鍵,鍵值默認為0
>>> c = dict.fromkeys(data,0)
遍歷隨機序列,遍歷到的值相應的鍵值加一
>>> for x in data:
... c[x] += 1
...
...
>>> c
{0: 2, 2: 1, 3: 2, 4: 1, 5: 3, 6: 1, 7: 1, 9: 1, 10: 1, 12: 1, 14: 3, 15: 2, 16: 3, 17: 2, 18: 2, 19: 3, 20: 1}
解決方案:
使用collections.Counter對象
將序列傳入Counter的構造器,得到Counter對象是元素頻度的字典
Counter.most_commom(n)方法得到頻度最高的n個元素的列表
首先看下Counter函數的語法:
┌──────────────────────────────────────────────────────────────────────────────────────────────┐
│ Counter: (*args, **kwds) │
│ data │
│ Create a new, empty Counter object. And if given, count elements │
│ from an input iterable. Or, initialize the count from another mapping │
│ of elements to their counts. │
│ │
│ >>> c = Counter() # a new, empty counter │
│ >>> c = Counter('gallahad') # a new counter from an iterable │
│ >>> c = Counter({'a': 4, 'b': 2}) # a new counter from a mapping │
│ >>> c = Counter(a=4, b=2) # a new counter from keyword args │
└──────────────────────────────────────────────────────────────────────────────────────────────┘
我們將生成的隨機序列傳入函數,返回的是一個統計結果的字典
>>> from collections import Counter
>>> c2 = Counter(data)
>>> c2
Counter({5: 3, 14: 3, 16: 3, 19: 3, 0: 2, 3: 2, 15: 2, 17: 2, 18: 2, 2: 1, 4: 1, 6: 1, 7: 1, 9: 1, 10: 1, 12: 1, 20: 1})
>>> c2[10]
1
>>> c2[5]
3
>>> c2.most_common(3)
[(5, 3), (14, 3), (16, 3)]
現在我們對一個文件進行詞頻統計:
>>> import re
將文件讀入為一個字符串
>>> txt = open('vimrc.txt').read()
使用非字符串對文件進行分割
>>> re.split('\W+',txt)
使用Counter函數對列表進行統計
>>> c3 = Counter(re.split('\W+',txt))
使用most_common統計前十頻度
>>> c3.most_common(10)
問題四:如何根據字典中值的大小,對字典中的項排序
問題內容:
某班英語成績以字典形式存儲為:
{'wex':98,'fyw':97,'xyx':99...}
根據成績高低,對學生排名
解決方案:
使用內置函數sorted
1,利用zip將字典數據轉化元組
2,傳遞sorted函數的key參數
方法一:使用zip轉換為元組進行排序
In [1]: from random import randint
In [2]: d = {x: randint(60,100) for x in 'xyzabc'}
對字典進行排序,只有鍵排序
In [3]: sorted(d)
Out[3]: ['a', 'b', 'c', 'x', 'y', 'z']
我們看對元組進行排序原理,先對第一個元素進行排序
In [4]: (97,'a') > (96,'b')
Out[4]: True
第一個相同,再多第二個進行排序
In [5]: (97,'a') > (97,'b')
Out[5]: False
In [6]: d.keys()
Out[6]: dict_keys(['x', 'b', 'z', 'y', 'c', 'a'])
In [7]: d.values()
Out[7]: dict_values([88, 76, 96, 78, 62, 68])
In [8]: zip(d.values(),d.keys())
Out[8]: <zip at 0x7f3aac585f48>
In [9]: type(d.keys())
Out[9]: dict_keys
在python3中,zip返回的值需要使用list轉換
In [10]: list(zip(d.values(),d.keys()))
Out[10]: [(88, 'x'), (76, 'b'), (96, 'z'), (78, 'y'), (62, 'c'), (68, 'a')]
In [11]: sorted(list(zip(d.values(),d.keys())))
Out[11]: [(62, 'c'), (68, 'a'), (76, 'b'), (78, 'y'), (88, 'x'), (96, 'z')]
方法二:傳遞sorted函數的key參數
當我們使用items()函數時,返回的元素列表的第一項并不是值
In [12]: d.items()
Out[12]: dict_items([('x', 88), ('b', 76), ('z', 96), ('y', 78), ('c', 62), ('a', 68)])
我們可以指定sorted的排序規則,下面函數中的key代表排序的元素,x為前面的元素列表,lambda函數返回的為字典的值
In [13]: sorted(d.items(),key=lambda x: x[1])
Out[13]: [('c', 62), ('a', 68), ('b', 76), ('y', 78), ('x', 88), ('z', 96)]
問題五:如何快速找到多個字典中的公共鍵(key)?
問題內容:
西班牙足球隊甲級聯賽,每一輪球員進球統計:
第一輪:{'蘇亞':1,'梅西':2,'本澤馬':1,......}
第二輪:{'蘇亞':2,'貝爾':1,'格里':2,......}
第三輪:{'蘇亞':1,'托爾':2,'貝爾':2,......}
......
統計出前N輪,每場比賽都有進球的球員。
使用簡單的遍歷:
>>> from random import randint,sample
使用sample選出隨機進球的球員
>>> sample('abcdefg',3)
['b', 'd', 'a']
設置進球球員為3到6人
>>> sample('abcdefg',randint(3,6))
['c', 'd', 'f', 'b', 'a', 'g']
使用字典生成式
>>> s1 = { x: randint(1,4) for x in sample('abcdefg',randint(3,6))}
>>> s1
{'f': 3, 'e': 3, 'c': 1, 'b': 4, 'g': 2}
>>> s2 = { x: randint(1,4) for x in sample('abcdefg',randint(3,6))}
>>> s3 = { x: randint(1,4) for x in sample('abcdefg',randint(3,6))}
>>> s2
{'f': 2, 'd': 1, 'c': 3, 'b': 3, 'g': 4}
>>> s3
{'e': 4, 'c': 3, 'a': 4, 'b': 3}
>>> inlist = []
>>> for k in s1:
... if k in s2 and k in s3:
... inlist.append(k)
...
...
>>> inlist
['c', 'b']
其他的解決方案:
利用集合(set)的交集操作:
步驟一:使用字典的viewkeys()方法,得到一個字典keys的集合(這是python2的做法,python3使用keys()方法獲得所有的鍵)
步驟二:使用map函數,得到所有的字典的keys的集合
步驟三:使用reduce函數,取得所有字典的keys的集合的交集
只有三輪比賽
>>> s1.keys()
dict_keys(['f', 'e', 'c', 'b', 'g'])
>>> s1.keys() & s2.keys() & s3.keys()
{'c', 'b'}
當有多輪的時候
>>> map(dict.keys,[s1,s2,s3])
<map object at 0x7f0993531c18>
使用map函數將字典的鍵都一次變為list
>>> reduce(lambda a,b: a & b,map(dict.keys,[s1,s2,s3]))
Traceback (most recent call last):
File "<input>", line 1, in <module>
reduce(lambda a,b: a & b,map(dict.keys,[s1,s2,s3]))
NameError: name 'reduce' is not defined
在Python 3里,reduce()函數已經被從全局名字空間里移除了,它現在被放置在fucntools模塊里 用的話要 先引
>>> from functools import reduce
>>> reduce(lambda a,b: a & b,map(dict.keys,[s1,s2,s3]))
{'c', 'b'}
問題六:如何讓字典保持有序?
問題內容:
編程競賽系統,對參賽選手編程解題進行計時,選手完成題目后,把該選手解題用時記錄到字典中,以便賽后按選手名查詢成績。(答題時間越短,成績越優)
{'wex':(2,43).'hmm':(5,43),'fyw':(1,23)......}
比賽結束后,需按排名順序依次打印選手成績,如何實現?
解決方案:
使用collections.OrderedDict
以OrderedDict替代內置字典Dict,依次將選手成績存入OrderedDict
from collections import OrderedDict
from random import randint
from time import time
d = OrderedDict()
players = list('ABCDEFGH')
start = time()
for i in range(8):
input()
p = players.pop(randint(0,7-i))
end = time()
print(i+1,p,end-start)
d[p] = (i+1,end-start)
print("******")
for k in d:
print(k,d[k])
輸出結果
1 G 1.6133077144622803
2 A 1.9757952690124512
3 D 2.3789286613464355
4 F 2.730335235595703
5 B 3.157444715499878
6 H 3.551867961883545
7 C 3.967583179473877
8 E 4.346914291381836
******
G (1, 1.6133077144622803)
A (2, 1.9757952690124512)
D (3, 2.3789286613464355)
F (4, 2.730335235595703)
B (5, 3.157444715499878)
H (6, 3.551867961883545)
C (7, 3.967583179473877)
E (8, 4.346914291381836)
問題七:如何實現用戶的歷史紀錄功能(最多n條)?
問題內容:
現在我們制作了一個簡單的猜數字的小游戲,添加歷史記錄功能,顯示用戶最近猜過的數字,如何實現?
解決方案:
使用容量為n的隊列存儲歷史紀錄
使用標準庫collectios中的deque,它是一個雙端循環隊列
程序退出前,可以使用pickle將隊列對象存入文件,再次運行程序時將其導入。
>>> from collections import deque
使用deque創建定長的隊列
>>> q = deque([],5)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q.append(4)
>>> q.append(5)
>>> q
deque([1, 2, 3, 4, 5], maxlen=5)
>>> q.append(6)
>>> q
deque([2, 3, 4, 5, 6], maxlen=5)
游戲文本:
from collections import deque
from random import randint
N = randint(0,100)
history = deque([],5)
def guess(k):
if k == N:
print('right')
if k < N:
print("%s is less-than N" % k)
else:
print("%s is greater-than N" % k)
return False
while True:
line = input("please input a number:")
if line.isdigit():
k = int(line)
history.append(k)
if guess(k):
break
elif line == "history":
print(list(history))
上面的程序能夠實現游戲的功能,并保存五次輸入。
使用pickle模塊的dump()和load()函數可以將數據保存到文件和從文件中讀取數據
>>> import pickle
>>> p = ['ew','ferf']
>>> pickle.dump(p,open('historyy','wb'))
>>> q2 = pickle.load(open('historyy','rb'))
>>> q2
['ew', 'ferf']