13-3.对类进行定制。写一个类,用来将浮点型值转换为金额。
class MoneyFmt(object):
def __init__(self, value=0.0, flag='-'):
self.value = float(value)
self.flag = flag
def dollarize(self):
"""转换"""
val = round(self.value, 2)
strvalue = str(val)
if strvalue.startswith('-'):
strvalue = strvalue[1:]
len = strvalue.find('.')
while (len-3) > 0:
strvalue = strvalue[:len-3] + ',' + strvalue[len-3:]
len -= 3
return self.flag + '$' + strvalue
len = strvalue.find('.')
while (len-3) > 0:
strvalue = strvalue[:len-3] + ',' + strvalue[len-3:]
len -= 3
return '$' + strvalue
def update(self, newvalue=None):
"""更新"""
if newvalue is not None:
self.value = float(newvalue)
def __nonzero__(self):
"非零"
if self.value == 0:
return False
return True
def __str__(self):
return self.dollarize()
def __repr__(self):
return repr(self.value)
13-4.用户注册。
建立一个用户数据库类,来管理一个系统,该系统要求用户在登录后才能访问某些资源。这个数据库类对用户进行管理,并在实例化操作时加载之前保存的用户信息,提供访问函数来添加或更新数据库的信息。在数据修改后,数据库会在垃圾回收时将新信息保存到磁盘。
from datetime import datetime
import shelve
class Userdatabase(object):
def __init__(self, dbfile):
"""创建数据库"""
self.dbfile = dbfile
self.db = shelve.open(self.dbfile, 'c')
self.db.close()
def newuser(self, user, pwd):
"""注册"""
self.db = shelve.open(self.dbfile, 'r')
if user in self.db:
print("User already exists")
self.db = shelve.open(self.dbfile, 'c')
self.db[user] = [user, pwd, datetime.now()]
self.db.close()
self.flag = False
def login(self, user, pwd):
"""登录"""
self.db = shelve.open(self.dbfile, 'r')
if user not in self.db:
self.flag = False
elif self.db[user][1] == pwd:
self.flag = True
self.db.close()
def deluser(self, user):
"""删除"""
self.db = {}
if self.flag:
self.db = shelve.open(self.dbfile, 'r')
if user in self.db:
self.db.pop(user)
else:
print('login first')
def updateuser(self, user, pwd):
"""更新"""
if self.flag:
self.db = shelve.open(self.dbfile, 'c')
self.db[user] = [user, pwd, datetime.now()]
self.db[user] = self.db[user]
else:
print('login first')
def listall(self):
"""查看"""
if self.flag:
for user in self.db:
print(self.db[user][0], self.db[user][1])
else:
print('login first')
if __name__ == '__main__':
user = Userdatabase('shelve.data')
user.newuser('admin', '123456')
user.login('admin', '123456')
user.updateuser('admin', '123456')
user.updateuser('test', '123456')
user.listall()
user.deluser('test')
user.listall()
13-5.几何。创建一个由有序数值对(x,y)组成的Point类,代表某个点的X和Y坐标。
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '(%d, %d)' % (self.x, self.y)
if __name__ == '__main__':
p = Point(1, 1)
print(p)
13-6.几何。创建一个直线类,除主要属性:一对坐标值外,它还具有长度和斜线属性。你需要覆盖__repr__()方法,使得代表那条直线的字符串表示形式是由一对元组构成的元组。
from math import sqrt
class Beeline(object):
def __init__(self, x1=0, y1=0, x2=0, y2=2):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
self.length = 0
self.slope = None
def getLength(self):
"""长度"""
if self.x1 == self.x2 and self.y1 == self.y2:
self.length = 0
elif self.x1 == self.x2:
self.length = abs(self.y2 - self.y1)
elif self.y1 == self.y2:
self.length = abs(self.x2 - self.x1)
else:
self.length = sqrt((self.y2 - self.y1)**2 + (self.x2 - self.x1)**2)
return self.length
def getSlope(self):
"""斜率"""
if self.length == 0:
self.slope = None
elif self.x1 == self.x2 or self.y1 == self.y2:
self.slope = None
else:
self.slope = float(self.y2 - self.y1) / (self.x2 - self.x1)
return self.slope
def __str__(self):
return '((%d, %d),(%d, %d))' % (self.x1, self.y1, self.x2, self.y2)
__repr__ = __str__
if __name__ == '__main__':
b = Beeline(1, 2, 3, 4)
print(b)
print('Length is %f' % b.getLength())
print('Slope is %s' % b.getSlope())
13-7.数据类。提供一个time模块的接口,允许用户按照自己给定的时间格式来查看日期。你的类应该维护一个日期值,并用给定的时间创建一个实例,如果没有给出时间值,程序执行时会默认采用当前的系统时间。
import time
class TimeFormat(object):
def __init__(self, t=time.time()):
self.mytime = t
def update(self, t=time.time()):
self.mytime = t
def display(self, ft=None):
fmt = {}
fmt['MDY'] = '%m/%d/%y'
fmt['MDYY'] = '%m/%d/%Y'
fmt['DMY'] = '%d/%m/%y'
fmt['DMYY'] = '%d/%m/%Y'
fmt['MODYY'] = '%b %d,%Y'
if ft in fmt:
return (time.strftime(fmt[ft], time.localtime(self.mytime)))
return time.ctime(self.mytime)
if __name__ == '__main__':
tf = TimeFormat()
print(tf.display())
print(tf.display('MDY'))
print(tf.display('MDYY'))
print(tf.display('DMY'))
print(tf.display('DMYY'))
print(tf.display('MODYY'))
tf.update(time.time() + 60)
print(tf.display())
13-8.堆栈类。
实现一个堆栈类,类中应该有push()和pop()方法,还有一个isempty()方法,如果堆栈是空的,返回布尔值1,否则返回0。
class Stack(object):
def __init__(self, l=[]):
self.l = l
def isempty(self):
if len(self.l) == 0:
return 1
return 0
def push(self, element):
print('pushed [', element, ']')
self.l.append(element)
def peek(self):
if self.isempty():
print('can not peek from an empty stack')
return self.l[0]
def pop(self):
if self.isempty():
print('can not pop from an empty stack')
else:
print('removed [', self.l.pop(), ']')
def viewstack(self):
print(self.l)
if __name__ == '__main__':
s = Stack()
s.viewstack()
s.pop()
s.viewstack()
s.push(2)
s.viewstack()
13-9.队列类。实现一个队列类,这个类必须支持下面几种方法:enqueue()在队列的尾部加入一个新的元素,dequeue()在队列的头部取出一个元素,返回它并且把它从列表中删除。
class Queue(object):
def __init__(self, l=[]):
self.l = l
def enqueue(self, element):
print('enter queue [', element, ']')
self.l.append(element)
def dequeue(self):
if self.l:
print('removed [', self.l.pop(0), ']')
else:
print('can not pop from an empty stack')
def viewstack(self):
print(self.l)
if __name__ == '__main__':
q = Queue([1, 2, 3])
q.viewstack()
q.enqueue(4)
q.dequeue()
q.viewstack()
13-10.堆栈和队列。编写一个类,定义一个能够同时具有堆栈和队列操作行为的数据结构。这个类和Perl语言中数组相像。需要实现四个方法。
class FifoLifo(object):
def __init__(self, l=[]):
self.l = l
def shift(self):
"""删除头"""
if self.l:
print('removed [', self.l.pop(0), ']')
else:
print('can not pop from an empty stack')
def unshift(self, element):
"""插入头"""
print('inserted [', element, ']')
self.l.insert(0, element)
def push(self, element):
"""插入尾"""
print('pushed [', element, ']')
self.l.append(element)
def pop(self):
"""删除尾"""
if self.l:
print('removed [', self.l.pop(), ']')
else:
print('can not pop from an empty stack')
def viewstack(self):
print(self.l)
if __name__ == '__main__':
f = FifoLifo([1, 2, 3, 4, 5])
f.viewstack()
f.shift()
f.unshift(6)
f.push(0)
f.pop()
f.viewstack()
13-11.电子商务。
你需要为一家B2C零售商编写一个基础的电子商务引擎。你需要写一个针对顾客的类User,一个对应存货清单的类Item,还有一个对应购物车的类叫Cart。货物放到购物车里,顾客可以有多个购物车。同时购物车里可以有多个货物,包括多个同样的货物。
class Item(object):
"""物品价格清单"""
def __init__(self, product, price):
self.product = product
self.price = price
def __str__(self):
return '(%s, %.2f)' % (self.product, self.price)
class Cart(object):
"""购物车"""
def __init__(self, cartname):
self.cartname = cartname
self.cartlist = {}
def appenditem(self, item, count=1):
"""增加数量"""
if item not in self.cartlist:
self.cartlist[item] = count
else:
self.cartlist[item] += count
def showcart(self):
"""查看"""
for i in self.cartlist:
print(self.cartname, i, self.cartlist[i])
def deleteitem(self, item, count=1):
"""减少数量"""
if item in self.cartlist and self.cartlist[item]>=count:
self.cartlist[item] -= count
if self.cartlist[item] == 0:
self.cartlist.pop(item)
class User(object):
"""顾客"""
def __init__(self, name):
self.name = name
self.userdb = []
def appendcart(self, cart):
self.userdb.append(cart.cartname)
def showuser(self):
print(self.name, self.userdb)
if __name__ == '__main__':
i1 = Item('huawei', 15000)
i2 = Item('iphone', 7000)
print(i1, i2)
c1 = Cart('cart1')
c2 = Cart('cart2')
c3 = Cart('cart3')
c1.appenditem(i1, 1)
c1.appenditem(i2, 1)
c2.appenditem(i2, 2)
c3.appenditem(i1, 2)
c1.showcart()
c2.showcart()
c3.showcart()
u1 = User('Tom')
u2 = User('Jerry')
u1.appendcart(c1)
u2.appendcart(c2)
u2.appendcart(c3)
u1.showuser()
u2.showuser()
13-12.聊天室。
你需要三个类:一个Message类,它包含一个消息字符串以及诸如广播、单方收件人等其他信息。一个User类,包含了进入你聊天室的某个人的所有信息。一个Room类,它体现了一个更加复杂的聊天系统,用户可以在聊天时创建单独的房间,并邀请其他人加入。
class Message(object):
def __init__(self, msg='', broadcast=False, froms='', to=''):
self.msg = msg
self.broadcast = broadcast
self.froms = froms
self.to = to
def __str__(self):
"""广播"""
if self.broadcast:
return 'message: %s from %s send to everyone' % (self.msg, self.froms)
else:
return 'message: %s from %s send to %s' % (self.msg, self.froms, self.to)
class User(object):
hear = {'everyone': ''}
def __init__(self, name, sex, age):
self.name = name
self.sex = sex
self.age = age
def __del__(self):
User.hear.clear()
def __str__(self):
return 'user:%s ,sex:%s ,age:%d' % (self.name, self.sex, self.age)
def talk(self, to='', msg=''):
"""发送广播"""
if to == 'everyone':
m = Message(msg, True, self.name)
User.hear['everyone'] = m
elif to:
m = Message(msg, False, self.name, to)
User.hear[to] = m
else:
print('receiver can not be empty')
def hearmsg(self):
"""显示消息"""
if self.name in User.hear:
print(User.hear[self.name])
elif User.hear['everyone']:
print(User.hear['everyone'])
else:
print('no msg for %s' % self.name)
def talkroom(self, room, to='', msg=''):
"""room广播"""
if to == 'everyone':
m = Message(msg, True, self.name)
room.receive(m)
elif to:
m = Message(msg, False, self.name, to)
room.receive(m)
else:
print('receiver can not be empty')
def hearroom(self, m):
"""显示room消息"""
print('room %s' % m)
def createroom(self, name, count=3):
"""room人员"""
return Room(name, count)
class Room(object):
def __init__(self, rname, count=3):
self.rname = rname
self.count = count
self.userlist = []
def adduser(self, user):
"""room用户邀请"""
if len(self.userlist) <= self.count:
self.userlist.append(user)
else:
print('user number limits')
def receive(self, m):
"""room广播"""
if m.broadcast:
print('room %s' % m)
else:
for user in self.userlist:
if user.name == m.to:
user.hearroom(m)
if __name__ == '__main__':
u1 = User('bob', 'male', 33)
u2 = User('jim', 'female', 31)
u3 = User('Tom', 'female', 31)
u1.talk('jim', 'hello')
u2.hearmsg()
u3.hearmsg()
room1 = u2.createroom('girls', 2)
room1.adduser(u2)
room1.adduser(u3)
u2.talkroom(room1, 'Tom', 'hello')
u3.talkroom(room1, 'everyone', 'hello')
13-13. 股票投资组合类。你的数据库中记录了每个公司的名字、股票代码、购买日期、购买价格和持股数量。需要编写的方法包括:添加新代号、删除代号、根据当前价格计算出的YTD或年回报率。
class Stock(object):
def __init__(self, name, code, date, price, amount):
self.name = name
self.code = code
self.date = date
self.price = price
self.amount = amount
def __str__(self):
return '%s %s %s %.2f %d' % (self.name, self.code, self.date, self.price, self.amount)
class Operator(object):
def __init__(self):
self.db = {}
def newcode(self, stock):
self.db.setdefault(stock.code, [stock.name, stock.date, stock.price, stock.amount])
def popcode(self, code):
self.db.pop(code)
def years(self, year):
if (year % 4 == 0 and year % 100 != 0) or (year % 4 == 0 and year % 400 == 0):
return True
def months(self, str1, str2):
month1 = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
month2 = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
bdate = str1.split('/')
bdate[0], bdate[1], bdate[2] = int(bdate[0]), int(bdate[1]), int(bdate[2])
edate = str2.split('/')
edate[0], edate[1], edate[2] = int(edate[0]), int(edate[1]), int(edate[2])
if bdate == edate:
return 0
elif bdate[1] == edate[1] and bdate[2] == bdate[2]:
return abs(bdate[0]-edate[0])
elif bdate[1] != edate[1] and bdate[2] == edate[2]:
bdays = 0
edays = 0
if self.years(bdate[2]):
for i in range(1, bdate[1]):
bdays += month2[i]
bdays = bdays + bdate[0]
for i in range(1, edate[1]):
edays += month2[i]
edays = edays + edate[0]
return abs(edays - bdays)
else:
for i in range(1, bdate[1]):
bdays += month1[i]
bdays = bdays + bdate[0]
for i in range(1, edate[1]):
edays += month1[i]
edays = edays + edate[0]
return abs(edays - bdays)
elif bdate[2] != edate[2]:
days = 0
for i in range(bdate[2]+1, edate[2]):
if self.years(i):
days += 366
else:
days += 365
if self.years(bdate[2]):
for i in range(bdate[1]+1, 13):
days += month2[i]
days += (month2[bdate[1]]-bdate[0])
else:
for i in range(bdate[1]+1, 13):
days += month1[i]
days += (month1[bdate[1] - bdate[0]])
if self.years(edate[2]):
for i in range(1, edate[1]):
days += month2[i]
days += edate[0]
return days
else:
for i in range(1, edate[1]):
days += month1[i]
days += edate[0]
return days
def YTD(self, stock, newprice, newdate):
holddays = self.months(stock.date, newdate)
costmoney = stock.price * stock.amount
curmoney = newprice * stock.amount
benifit = curmoney - costmoney
if stock.code in self.db:
print('YTD of %s is %.2f %%' %(stock.code, benifit/holddays*365/costmoney))
else:
print('Stock %s does not exists' % stock.code)
def showdb(self):
print(self.db)
s1 = Stock('xx', '123', '01/01/2019', 1.0, 1000)
s2 = Stock('yy', '234', '05/01/2019', 18.0, 1000)
op1 = Operator()
op1.newcode(s1)
op1.newcode(s2)
op1.showdb()
op1.popcode('234')
op1.showdb()
op1.YTD(s1, 1.01, '11/03/2019')
13-14.DOS。为DOS机器编写一个Unix操作界面的shell。你向用户提供一个命令行,使得用户可以在那里输入unix命令,你可以对这些命令进行解释,并把返回相应的输出。
import os
class Shell(object):
def __init__(self):
self.cmddict = {'ls': 'dir', 'more': 'more', 'cat': 'type', 'cp': 'copy', 'mv': 'ren', 'rm': 'del'}
def translate(self, cmd):
opt = cmd.split()
if opt[0] in self.cmddict:
opt[0] = self.cmddict[opt[0]]
return ' '.join(opt)
def start(self):
while True:
cmd = input('#')
cmd = self.translate(cmd)
if cmd == 'exit':
break
output = os.popen(cmd).readlines()
for out in output:
print(out)
if __name__ == '__main__':
s = Shell()
s.start()
13-16.授权和函数编程。
(a)请为示例中的CanOpen类编写一个writelines()方法,这个新函数可以一次读入多行文本,然后将文本转化为大写的形式。
(b)在writelines()方法中添加一个参数,用这个参数来指明是否需要为每行文本加上一个换行符。此参数的默认值是False,表示不加换行符。
import os
class CapOpen(object):
def __init__(self, fn, mode='r', buf=-1):
self.file = open(fn, mode, buf)
def __str__(self):
return str(self.file)
def __repr__(self):
return 'self.file'
def write(self, line):
self.file.write(line.upper())
def writelines(self, lines, enter=False):
for line in lines:
if enter:
line += os.linesep
self.write(line)
def __getattr__(self, attr):
return getattr(self.file, attr)
if __name__ == '__main__':
f1 = CapOpen('test.txt')
lines = f1.readlines()
f1.close()
f2 = CapOpen('newtest.txt', 'w')
f2.writelines(lines, False)
f2.close()
13-17. 数值类型子类化。在示例13.3中所看到的moneyfmt.py脚本基础上修改它,使得它可以扩展Python的浮点类型。请确保它支持所有操作,而且是不可变的。
class MoneyFmt(float):
def __new__(cls, value=0.0, flag='-'):
cls.flag = flag
cls.value = super(MoneyFmt, cls).__new__(cls, value)
return cls.value
def dollarize(cls):
"""转换"""
val = round(cls.value, 2)
strvalue = str(val)
if strvalue.startswith('-'):
strvalue = strvalue[1:]
len = strvalue.find('.')
while (len-3) > 0:
strvalue = strvalue[:len-3] + ',' + strvalue[len-3:]
len -= 3
return cls.flag + '$' + strvalue
len = strvalue.find('.')
while (len-3) > 0:
strvalue = strvalue[:len-3] + ',' + strvalue[len-3:]
len -= 3
return '$' + strvalue
def __nonzero__(cls):
"非零"
if cls.value == 0:
return False
return True
def __str__(cls):
return cls.dollarize()
if __name__ == '__main__':
money = input('Money: ')
moneyfmt = MoneyFmt(money)
print(moneyfmt.dollarize())
13-18. 序列类型子类化。模仿前面练习13-4中的用户注册类的解决方案,编写一个子类。要求允许用户修改密码,但密码有效期是12个月,过期后不能重复使用。
import time, shelve
class Userdatabase(object):
def __init__(self, dbfile):
"""创建数据库"""
self.dbfile = dbfile
self.db = shelve.open(self.dbfile, 'c')
self.db.close()
def newuser(self, user, pwd):
"""注册"""
self.db = shelve.open(self.dbfile, 'r')
if user in self.db:
print("User already exists")
self.db = shelve.open(self.dbfile, 'c')
self.db[user] = [user, [pwd, time.time()], time.ctime()]
self.db.close()
self.flag = False
def login(self, user, pwd):
"""登录"""
self.db = shelve.open(self.dbfile, 'r')
if user not in self.db:
self.flag = False
elif self.db[user][1][0] == pwd:
if self.db[user][1][-1] - time.time() > 365*24*60*60:
print("Password has expired")
self.flag = False
else:
self.flag = True
self.db.close()
def deluser(self, user):
"""删除"""
self.db = {}
if self.flag:
self.db = shelve.open(self.dbfile, 'r')
if user in self.db:
self.db.pop(user)
else:
print('login first')
def updateuser(self, user, pwd):
"""更新密码"""
newdb = {}
if self.flag:
self.db = shelve.open(self.dbfile, 'r')
if pwd in self.db[user][1]:
print("The new password cannot be the same as the used password")
else:
value = [user, self.db[user][1], time.ctime()]
value[1].insert(0, pwd)
value[1][-1] = time.time()
newdb[user] = value
self.db = shelve.open(self.dbfile, 'c')
self.db[user] = newdb[user]
print(self.db[user])
else:
print('login first')
def listall(self):
"""查看"""
if self.flag:
for user in self.db:
print(self.db[user][0], self.db[user][1][0])
else:
print('login first')
if __name__ == '__main__':
user = Userdatabase('shelve.data')
user.newuser('admin', '123456')
user.newuser('test', '123456')
user.login('admin', '123456')
user.login('test', '123456')
user.updateuser('admin', '123456')
user.updateuser('test', '123456')
user.listall()
user.deluser('test')
user.listall()
13-20.类的定制。改进脚本time60.py
(a)允许“空”实例化:如果小时和分钟的值没有给出,默认为0小时0分钟。
(b)用0占位组成两位数的形式,因为当前的时间格式不符合要求。
(c)除了用hours(hr)和minutes(min)进行初始化外,还支持以下时间输入格式:
一个由小时和分钟组成的元组,如(10,30)
一个由小时和分钟组成的字典,如{'hr':10, 'min':30}
一个代表小时和分钟的字符串,如"10:30"
(e)实现__repr__()。
(f)添加60进制的运算功能。
class Time60(object):
'Time60 - track hours and minutes'
def __init__(self, *time):
'Time60 constructor - takes hours and minutes'
if len(time) == 1:
if type(time[0]) == tuple:
self.hr = time[0][0]
self.min = time[0][1]
elif type(time[0]) == dict:
self.hr = time[0]['hr']
self.min = time[0]['min']
elif type(time[0]) == str:
all = time[0].split(':')
self.hr = int(all[0])
self.min = int(all[1])
elif len(time) == 0:
self.hr = 0
self.min = 0
else:
self.hr = time[0]
self.min = time[1]
def __str__(self):
'Time60 - string representation'
return '%02d:%02d' % (self.hr, self.min)
def __repr__(self):
return repr('%02d:%02d' % (self.hr, self.min))
def __add__(self, other):
'Time60 - overloading the addition operator'
hr = self.hr + other.hr
min = self.min + other.min
ahr = min // 60
min %= 60
hr += ahr
if hr > 23: hr = 0
return self.__class__(hr, min)
def __iadd__(self, other):
'Time60 - overloading in-place addition'
self.hr += other.hr
self.min += other.min
return self
print(Time60())
print(Time60(10, 30))
print(Time60("15:31"))
print(Time60((10, 30)))
print(Time60({'hr':11, 'min':15}))
print(Time60(1, 5))
print(Time60("15:31") + Time60(8,31))