《Python核心編程》第7章 映像和集合類型 練習

7–1. 字典方法。

哪個字典方法可以用來把兩個字典合併到一起?

#update方法
7–2. 字典的鍵。我們知道字典的值可以是任意的Python 對象,那字典的鍵又如何呢?請試着將除數字和字符串以外的其他不同類型的對象作爲字典的鍵,看一看,哪些類型可以,哪些不行?對那些不能作字典的鍵的對象類型,你認爲是什麼原因呢?

#字典的健必須是可哈希的對象,包括所有不可變類型,和實現了__hash__()特殊方法的類。對於列表之類的可變對象,不能作爲字典的鍵。因爲解釋器調用哈希函數,來根據鍵值計算得到數據的存儲位置,如果鍵是可變的,那麼位置的計算就會不可靠。
7–3. 字典和列表的方法。
(a) 創建一個字典,並把這個字典中的鍵按照字母順序顯示出來。
(b) 現在根據已按照字母順序排序好的鍵,顯示出這個字典中的鍵和值。
(c)同(b),但這次是根據已按照字母順序排序好的字典的值,顯示出這個字典中的鍵和值。(注意:對字典和哈希表來說,這樣做一般沒有什麼實際意義,因爲大多數訪問和排序(如果需要)都是基於字典的鍵,這裏只把它作爲一個練習。)
dc = {'hell': 'x', 'hello': 'tree', 'test': 'failed'}
for x in sorted(dc):
    print x,

for x in sorted(dc):
    print 'key=', x, 'value=', dc[x]

items = dc.items()
for value in sorted(dc.values()):
    for k, v in items:
        if value == v:
            print 'key=', k, 'value=', v
            items.remove((k, v))
            break

7-4. 建立字典。

給定兩個長度相同的列表,比如說,列表[1, 2, 3,...]和['abc', 'def','ghi',...],用這兩個列表裏的所有數據組成一個字典,像這樣:{1:'abc', 2: 'def', 3: 'ghi',...}

def build_dict(list1, list2):
    assert len(list1) == len(list2)
    return dict(zip(list1, list2))

if __name__ == '__main__':
    list1 = [1, 2, 3, 4]
    list2 = ['abc', 'def', 'ghi', 'jkl']
    print build_dict(list1, list2)
7–5. userpw2.py.

下面的問題和例題7.1 中管理名字-密碼的鍵值對數據的程序有關。
(a)修改那個腳本,使它能記錄用戶上次的登錄日期和時間(用time 模塊),並與用戶密碼一起保存起來。程序的界面有要求用戶輸入用戶名和密碼的提示。無論戶名是否成功登錄,都應有提示,在戶名成功登錄後,應更新相應用戶的上次登錄時間戳。如果本次登錄與上次登錄在時間上相差不超過4 個小時,則通知該用戶: “You already logged in at: <last_ login_timestamp>.”
(b) 添加一個“管理”菜單,其中有以下兩項:(1)刪除一個用戶 (2)顯示系統中所有用戶的名字和他們的密碼的清單。
(c) 口令目前沒有加密。請添加一段對口令加密的代碼(請參考crypt, rotor, 或其它加密模塊)
(d) 爲程序添加圖形界面,例如,用Tkinter 寫。
(e) 要求用戶名不區分大小寫。
(f) 加強對用戶名的限制,不允許符號和空白符。
(g)合併“新用戶”和“老用戶”兩個選項。如果一個新用戶試圖用一個不存在的用戶名登錄,
詢問該用戶是否是新用戶,如果回答是肯定的,就創建該帳戶。否則,按照老用戶的方式登錄。

#!/usr/bin/env python

import time
import md5
import base64

db = {}
log = {}

def newuser():
    prompt = 'login desired: '
    while True:
        name = raw_input(prompt).lower()
        if not checkname(name):
            prompt = 'invalid name, try again: '
            continue
        if db.has_key(name):
            prompt = 'name taken, try another: '
            continue
        else:
            break
    pwd = raw_input('password: ')
    db[name] = encodepwd(pwd)

def olduser():
    name = raw_input('login: ').lower()
    if name not in db:
        choice = raw_input('register a new user? y/n')
        if choice == 'y':
            newuser()
        return

    pwd = raw_input('password: ')
    passwd = db.get(name)
    if passwd == encodepwd(pwd):
        if time.time() - log.get(name, 0) < 4 * 3600:
            print 'You already logged in at: %s' % time.ctime(log.get(name))
        else:
            print 'welcome back', name
        log[name] = time.time()
    else:
        print 'login incorrect'

def deluser():
    name = raw_input('which user: ')
    if name in db:
        del db[name]
    if name in log:
        del log[name]
    print 'delete user succeed'

def listusers():
    for key in db:
        print key, db[key]

def encodepwd(passwd):
    pwd = md5.new()
    pwd.update(passwd)
    return base64.encodestring(pwd.digest())

def checkname(name):
    return name.isalnum()
        

def showmenu():
    prompt = """
(U)User Login
(D)elte User
(L)ist Users
(Q)uit
Enter choice:"""

    done = False
    while not done:
        chosen = False
        while not chosen:
            try:
                choice = raw_input(prompt).strip()[0].lower()
            except (EOFError, KeyboardInterrupt):
                choice = 'q'
            print '\nYour picked: [%s]' % choice
            if choice  not in 'udlq':
                print 'invalid option, try again'
            else:
                chosen = True

        if choice == 'q': done = True
        if choice == 'd': deluser()
        if choice == 'l': listusers()
        if choice == 'u': olduser()

if __name__ == '__main__':
    showmenu()
7-6. 列表和字典。

創建一個簡單的股票證券投資數據系統。其中應至少包含四項數據:股市行情顯示器符號,所持有的股票,購買價格及當前價位 - 你可以隨意添加其他數據項,比如收益率,
52 周最高指數、最低指數,等等。用戶每次輸入各列的數據構成一個輸出行。每行數據構成一個列表。還有一個總列表,包括了所有行的數據。數據輸入完畢後,提示用戶選擇一列數據項進行排序。把該數據項抽取出來作爲字典的鍵,字典的值就是該鍵對應行的值的列表。提醒讀者:被選擇用來排序的數據項必須是非重複的鍵,否則就會丟失數據,因爲字典不允許一個鍵有多個值。你還可以選擇其他計算輸出,比如,盈虧比率,目前證券資產價值等。

info = []
while True:
    market = raw_input('market: ')
    if market == '-1': break
    stock = raw_input('stock: ')
    price = float(raw_input('price: '))
    current = float(raw_input('current: '))
    info += [[market, stock, price, current]]

order = {'market': 0, 'stock': 1, 'price': 2, 'current': 3}
col = order.get(raw_input('item: '), 0)
key = zip(*info)[col]
data = dict(zip(key, info))
key = list(key)
key.sort()

for k in key:
    print data[k]
7-7. 顛倒字典中的鍵和值。

用一個字典做輸入,輸出另一個字典,用前者的鍵做值,前者的值做鍵。

d1 = {1: 'apple', 2: 'microsoft', 3: 'yahoo', 4: 'google'}
data = zip(*d1.items())
data.reverse()
d2 = dict(zip(*data))
print d2
7-8. 人力資源。

創建一個簡單的僱員姓名和編號的程序。讓用戶輸入一組僱員姓名和編號。你的程序可以提供按照姓名排序輸出的功能,僱員姓名顯示在前面,後面是對應的僱員編號。附加
題:添加一項功能,按照僱員編號的順序輸出數據。

db1 = {}
db2 = {}
while True:
    name = raw_input('name: ')
    if name == '-1': break
    pid = raw_input('id: ')
    db1[name] = pid
    db2[pid] = name

for key in sorted(zip(*db1.items())[0]):
    print key, db1[key]

for key in sorted(zip(*db2.items())[0]):
    print key, db2[key]

7-9. 翻譯
(a) 編寫一個字符翻譯程序(功能類似於Unix 中的tr 命令)。我們將這個函數叫做tr(),它有三個字符串做參數: 源字符串、目的字符串、基本字符串,語法定義如下:
def tr(srcstr, dststr, string)
srcstr 的內容是你打算“翻譯”的字符集合,dsrstr 是翻譯後得到的字符集合,而string 是你打算進行翻譯操作的字符串。舉例來說,如果srcstr == 'abc', dststr == 'mno', string =='abcdef', 那麼tr()的輸出將是'mnodef'. 注意這裏len(srcstr) == len(dststr).
在這個練習裏,你可以使用內建函數chr() 和 ord(), 但它們並不一定是解決這個問題所必不可少的函數。
(b) 在這個函數裏增加一個標誌符參數,來處理不區分大小寫的翻譯問題。
(c)修改你的程序,使它能夠處理刪除字符的操作。字符串srcstr 中不能夠映射到字符串dststr中字符的多餘字符都將被過濾掉。換句話說,這些字符沒有映射到dststr 字符串中的任何字符,因此就從函數返回的字符裏被過濾掉了。舉例來說:如果 srcstr == 'abcdef', dststr == 'mno',string == 'abcdefghi', 那麼tr()將輸出'mnoghi'. 注意這裏len(srcstr) >= len(dststr).

def tr(srcstr, dststr, string, casemap=True):
    assert len(srcstr) >= len(dststr)

    table = dict(zip(srcstr, dststr))
    if len(srcstr) > len(dststr):
        temp = {}.fromkeys(srcstr[len(dststr):])
        table.update(temp)

    ls = []
    for ch in string:
        if not casemap:
            if ch.lower() in table:
                ls.append(table[ch.lower()])
            elif ch.upper() in table:
                ls.append(table[ch.upper()])
            else:
                ls.append(ch)
            continue
        
        if ch in table:
            ls.append(table[ch])
        else:
            ls.append(ch)

    ls = [ch for ch in ls if ch]
    return ''.join(ls)

print tr('abc', 'mno', 'abcdef')
print tr('aBcdef', 'mno', 'abcdefghi', False)
7–10. 加密。
(a) 用上一個練習的思路編寫一個"rot13"翻譯器。"rot13"是一個古老而又簡單的加密方法,它把字母表中的每個字母用其後的第13 個字母來代替。字母表中前半部分字母將被映射到後半部分,而後半部分字母將被映射到前半部分,大小寫保持不變。舉例來說,'a'將被替換爲'n','X'將被替換爲'K'; 數字和符號不進行翻譯。
(b)在你的解決方案的基礎上加一個應用程序,讓它提示用戶輸入準備加密的字符串(這個算法同時也可以對加密後的字符串進行解密),如下所示:
% rot13.py
Enter string to rot13: This is a short sentence. Your string to en/decrypt was: [This
is a short sentence.].
The rot13 string is: [Guvf vf n fubeg fragrapr.].
%
% rot13.py
Enter string to rot13: Guvf vf n fubeg fragrapr. Your string to en/decrypt was: [Guvf
vf n fubeg fragrapr.].
The rot13 string is: [This is a short sentence.].
def rot13(srcstr):
    ls1 = [chr((num + 13) % 26 + ord('a')) for num in range(26)]
    ls2 = [chr(num + ord('a')) for num in range(26)]
    table = dict(zip(ls2, ls1))
    ls1 = "".join(ls1).upper()
    ls2 = "".join(ls2).upper()
    table.update(zip(ls2, ls1))

    ls = []
    for ch in srcstr:
        if ch in table:
            ls.append(table[ch])
        else:
            ls.append(ch)
    return "".join(ls)
7–13. 隨機數。

修改練習5-17 的代碼:使用random 模塊中的randint()或randrange()方法生成一個隨機數集合:從0 到9(包括9)中隨機選擇,生成1 到10 個隨機數。這些數字組成集合
A(A 可以是可變集合,也可以不是)。同理,按此方法生成集合B。每次新生成集合A 和B 後,顯示結果 A | B 和 A & B

from random import randint

ls1 = [randint(0, 9) for i in range(randint(1, 10))]
ls2 = [randint(0, 9) for i in range(randint(1, 10))]
a = frozenset(ls1)
b = frozenset(ls2)
print a & b
print a | b
7–15. 編寫計算器。

這個練習取材於http://math.hws.edu/ 在線免費Java 教材中的練習12.2。編寫一個程序允許用戶選擇兩個集合:A 和B, 及運算操作符。例如,in, not in, &, |, ^, <,<=, >, >=, ==, !=, 等. (你自己定義集合的輸入語法,它們並不一定要像Java 示例中那樣用方括號括住。)解析輸入的字符串,按照用戶選擇的運算進行操作。你寫的程序代碼應該比Java 版本的
該程序更簡潔。

def compute(a, b, op):
    foo = {}
    foo['in'] = lambda x, y: x in y
    foo['not in'] = lambda x, y: x not in y
    foo['&'] = lambda x, y: x & y
    foo['|'] = lambda x, y: x | y
    foo['^'] = lambda x, y: x ^ y
    foo['<'] = lambda x, y: x < y
    foo['<='] = lambda x, y: x <= y
    foo['>'] = lambda x, y: x > y
    foo['>='] = lambda x, y: x >= y
    foo['=='] = lambda x, y: x == y
    foo['!='] = lambda x, y: x != y
    foo['-'] = lambda x, y: x - y
    if op not in foo:
        return None
    return foo[op](a, b)

def main():
    ls1 = eval(raw_input('A: '))
    ls2 = eval(raw_input('B: '))
    a = set(ls1)
    b = set(ls2)
    op = raw_input('operator: ')
    print compute(a, b, op)

if __name__ == '__main__':
    main()

發佈了40 篇原創文章 · 獲贊 8 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章