《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: .”
(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()

你可能感兴趣的:(Python)