参考链接:
【python核心编程笔记+习题】-CH13-面向对象编程-上 https://blog.youkuaiyun.com/csdnhnma/article/details/103849113
python基础教程:https://www.runoob.com/python/python-func-open.html
python 100问: https://www.sohu.com/a/307743429_571478
13-13. 股票投资组合类.你的数据库中记录了每个公司的名字,股票代号,购买日期,购买价格和持股数量。需要编写的方法包括:添加新代号(新买的股票)、删除代号(所有卖出股票),根据当前价格(及日期)计算出的YTD或年回报率。
#股票应有一个市场价格,每日变动,这个地方我设成定值2.0
class stock(object):
price = 2.0
def __init__(self,name,id):
self.name=name
self.id=id
#我的股票应该记录的是某个用户手里的股票
# 添加新代号(新买的股票)、删除代号(所有卖出股票),根据当前价格(及日期)计算出的YTD或年回报率
class mystock(object):
db=[]
profit=0.0
def add(self,name,id,date,number):
st=stock(name,id)
price=st.price
mylist=[st,price,date,number]
self.db.append(mylist)
# 删除数量为0的股票
def delstock(self):
for i in self.db:
if i[3]==0:
st.db.remove(i)
# 卖出后 计算 利润
def sub(self,name,subnum):
for i in self.db:
if i[0].name == name:
i[3]=i[3]-subnum
self.profit=(i[0].price-i[1])*subnum+self.profit
13-14. DOS. 为DOS机器编写一个UNIX操作界面的shell。你向用户提供一个命令行,使得用户可以在那里输入Unix命令,你可以对这些命令进行解释,并返回相应的输出,例如:“ls”命令调用“dir”来显示一个目录中的文件列表,“more”调用同名命令(分页显示一个文件),“cat” 调用 “type,” “cp” 调用“copy,” “mv” 调用 “ren,” “rm” 调用 “del,” 等.
class Shell(object):
def __init__(self):
self.cmds = {'ls':'dir','more':'more','cat':'type','cp':'copy','mv':'ren','rm':'del'}
#单次输入一个命令,转换一个命令,故此处只编码实现单个命令的转换功能
def translate(self,cmd):
opt = cmd.split()
if opt[0] in self.cmds:
opt[0] = self.cmds[opt[0]]
return ' '.join(opt)
def start(self):
while 1:
cmd=raw_input('#')
cmd=self.translate(cmd)
if cmd == 'exit':
break
else:
os.system(cmd)
if __name__=='__main__':
s=Shell()
s.start()
13-15. 授权。示例13.8的执行结果表明我们的类CapOpen能成功完成数据的写入操作。在我们的最后评论中,提到可以使用CapOpen() 或 open()来读取文件中的文本。为什么呢?这两者使用起来有什么差异吗?
两者是没有差别的,因为在CapOpen中没有覆盖readlines方法。还是调用的file对象提供的。
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 __getattr__(self, item):
return getattr(self.file,item)
if __name__=='__main__':
f=CapOpen(r'C:\Users\Administrator\Downloads\test.txt')
print f
lines=f.readlines()
for i in lines:
print i,
f.close()
g = open(r'C:\Users\Administrator\Downloads\test.txt')
print g
lines = g.readlines()
for j in lines:
print j,
g.close()
13-16. 授权和函数编程。
(a) 请为示例 13.8 中的 CapOpen 类编写一个 writelines()方法。这个新函数将可以一次读入多行文本,然后将文本数据转换成大写的形式,它与 write()方法的区别和通常意思上的 writelines()与 write()方法之间的区别相似。注意:编写完这个方法后,writelines()将不再由文件对象"代理"。
(b) 在 writelines()方法中添加一个参数,用这个参数来指明是否需要为每行文本加上一个换行符。此参数的默认值是 False,表示不加换行符。
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,list,index=False): #这个新函数将可以一次读入多行文本,然后将文本数据转换成大写的形式
# 根据换行标记来设置每行结束是否写换行符
if index == False:
tem = ''
else:
tem = '\n'
for i in list:
self.file.write(i.upper())
self.file.write(tem)
def __getattr__(self, attr):
return getattr(self.file,attr)
if __name__=='__main__':
lines = ['line1', 'line2', 'line3', 'line4']
f=CapOpen('c:/Users/Administrator/Downloads/test.txt','w')
f.writelines(lines)
f.close()
print f
13-17. 数值类型子类化。在示例 13.3 中所看到的 moneyfmt.py 脚本基础上修改它,使得它
可以扩展 Python 的浮点类型。请确保它支持所有操作,而且是不可变的。
def update2(self, value=None):
st = str(value)
index = st.index('.')
st1 = st[:index]
st2 = st[index+1:]
st3 = st1+st2
st4 = st3[:-2]
yu = int(st3) % 10
if yu >= 5:
uvalue = int(st4)+1
else:
uvalue = int(st4)
re =uvalue* 10/(10*len(st2))
self.value=re
return self.value
13-18. 序列类型子类化。模仿前面练习 13-4 中的用户注册类的解决方案,编写一个子类。
要求允许用户修改密码,但密码的有效期限是 12 个月,过期后不能重复使用。附加题:支持“相
似密码”检测的功能(任何算法皆可),不允许用户使用与之前 12 个月期间所使用的密码相似的任何
密码。
只写了思路
import time
#定义用户类,包括构造函数、删除函数,还有 登录方法、添加用户方法
class userdb(object):
db={}
psdb={}
index=False
def __init__(self):
f=open('test.txt','r')
lines=f.readlines()
f.close()
for line in lines:
st1 =line.split(':') #split 返回的是list
st2=st1[1].split(',')
name=st1[0]
passwd=int(st2[0])
time=st2[1].strip()
li=[]
li.append(passwd)
li.append(time)
self.db[name]=li
#登录
def login(self,name,passwd):
if self.db[name][0] == passwd:
#判断密码是否过期
li=self.psdb[name]
for i in li:
if i[0]==passwd:
if time.localtime()-i[1]>12*30*2*60*60:
print 'passwd out of time'
break
else:
print 'your last time login:',self.db[name][1]
self.db[name][1] = time.strftime("%Y %m %d %H %M", time.localtime()) #strftime()函数将时间格式化为我们想要的格式
self.index = True
else:
print 'login fail'
#添加
def adduser(self,newname,newpwd):
if self.index==True:
ti= time.strftime("%Y %m %d %H %M", time.localtime())
li=[]
li.append(newpwd)
li.append(ti)
self.db[newname]=li
#这个密码的初始时间
self.psdb[newname] = [[newpwd,time.localtime()]]
else:
print 'you have not lgin in'
def __del__(self):
f=open('test.txt','w')
for keys in self.db:
st=keys+':'+str(self.db[keys][0])+','+self.db[keys][1]
f.write(st)
f.write('\n')
f.close()
#定义一个类,修改用户密码
class changePd(userdb):
def __init__(self,uname):
userdb.__init__(self)
self.uname=uname
def pdchange(self,newpd):
# 判断是否更改了大小写,可用严密的相似度比较算法---余弦相似度 https://blog.youkuaiyun.com/zz_dd_yy/article/details/51926305
li = self.psdb[self.uname]
for i in li:
#当两个密码相似且时间间隔不满一年
if i[0].upper()== newpd.upper() and time.localtime()-i[1]<12*30*24*60*60:
print 'similar passwd in one year'
break
else:
self.db[self.uname][0]=newpd
self.savefile()
def savefile(self):
f=open('test.txt','w')
for keys in self.db:
st = keys+':'+str(self.db[keys][0])+','+self.db[keys][1]
f.write(st)
f.write('\n')
f.close()
if __name__ =='__main__':
userdb=userdb()
userdb.adduser('Anna','111111')
userdb.login('Anna','111111')
# user = changePd('Anna')
# user.pdchange('123456')
# olduser = userdb()
# Anna:111111,'2021-5-15'
13-19. 映射类型子类化。假设在 13.11.3 节中字典的子类,若将 keys()方法重写为:
def keys(self):
return sorted(self.keys())
回顾知识点(13.11.3 从标准类型派生):
子类化的不可变类型例子:
class RoundFloat(float):
def __new__(cls, val):
return float.__new__(cls, round(val, 2))
我们覆盖了__new__()特殊方法来定制我们的对象,使之和标准 Python 浮点数(float)有一些区别:我们使用 round()内建函数对原浮点数进行舍入操作,然后实例化我们的 float,RoundFloat。我们是通过调用父类的构造器来创建真实的对象的。
__new()__方法都是类方法,我们要显式传入类传为第一个参数,这类似于常见的方法如__init__()中需要的 self
好是使用 super()内建函数去捕获对应的父类以调用它的__new()__方法,下面,对它进行这方面的修改:
class RoundFloat(float):
def __new__(cls, val):
return super(RoundFloat, cls).__new__(cls, round(val, 2))
子类化的可变类型的例子
一般情况下,你所继承到的类型的默认行为就是你想要的。下例中,我们简单地创建一个新的字典类型,它的 keys()方法会自动排序结果:
class SortedKeyDict(dict):
def keys(self):
return sorted(super( SortedKeyDict, self).keys())
使用:
通过 keys 迭代过程是以散列顺序的形式,而使用我们(重写的)keys()方法则将keys 变为字母排序方式了。
** 字典(dictionary)可以由 dict(),dict(mapping),dict(sequence_of_2_tuples),或者 dict(**kwargs)来创建
(a) 当方法 keys()被调用,结果如何?
就会陷入无限循环
(b) 为什么会有这样的结果?如何使我们的原解决方案顺利工作
因为重写了.keys方法, self.keys会首先调用子类的.keys方法,而子类的.keys方法中又调用了自己,形成了循环。这个地方必须申明调用父类的.keys方法才能取到键的值。
13-20. 类的定制。改进脚本 time60.py,见 13.13.2 节,示例 13.3.
(a) 允许“空”实例化: 如果小时和分钟的值没有给出,默认为零小时、零分钟。
(b) 用零占位组成两位数的表示形式,因为当前的时间格式不符合要求。如下面的示例,wed应该输出为“12:05.”
>>> wed = Time60(12, 5)
>>> wed
12:5
(c)除了用 hours (hr) 和 minutes (min)进行初始化外,还支持以下时间输入格式:
� 一个由小时和分钟组成的元组(10, 30)
� 一个由小时和分钟组成的字典({'hr': 10, 'min': 30})
� 一个代表小时和分钟的字符串("10:30")
附加题: 允许不恰当的时间字符串表示形式,如 “12:5”.
(d) 我们是否需要实现__radd__()方法? 为什么? 如果不必实现此方法,那我们什么时候可以或应该覆盖它?
radd方法不用在这里实现,它是一个自定义的类操作符,作用是在a+b时(a,b都是实例),当a中没有定义add方法时,就在b中找radd操作符
add定义该类实例的加法(a+b中的a)
iadd定义该实例用于自加语法时的操作方法 a += b
(e) __repr__()函数的实现是有缺陷而且被误导的。我们只是重载了此函数,这样我们可以省去使用 print 语句的麻烦,使它在解释器中很好的显示出来。但是,这个违背了一个原则:对于可估值的 Python 表达式,repr()总是应该给出一个(有效的)字符串表示形式。12:05 本身不是一个合法的 Python 表达式,但 Time60('12:05')是合法的。请实现它。
(f) 添加六十进制(基数是 60)的运算功能。下面示例中的输出应该是 19:15,而不是 18:75:
>>> thu = Time60(10, 30)
>>> fri = Time60(8, 45)
>>> thu + fri
18:75
'''
(c)除了用 hours (hr) 和 minutes (min)进行初始化外,还支持以下时间输入格式:
� 一个由小时和分钟组成的元组(10, 30)
� 一个由小时和分钟组成的字典({'hr': 10, 'min': 30})
� 一个代表小时和分钟的字符串("10:30")
(f) 添加六十进制(基数是 60)的运算功能。下面示例中的输出应该是 19:15,而不是 18:75
'''
class Time60(object):
# def __init__(self,hr,min):
# self.hr=hr
# self.min=min
def __init__(self,*rest):
# 特殊格式输入处理
if len(rest)==1:
# rest这里是一个tuple类型,以下是元组的切片调用形式
if type(rest[0])==tuple:
self.hr=rest[0][0]
self.min=rest[0][1]
elif type(rest[0]) == dict:
self.hr=rest[0]['hr']
self.min = rest[0]['min']
elif type(rest[0]) == str:
index = rest[0].index(':')
self.hr=int(rest[0][:index])
self.min = int(rest[0][index+1:])
# 无输入
elif len(rest) == 0:
self.hr = 0
self.min = 0
# 正常输入
else:
self.hr=rest[0]
self.min=rest[1]
def __str__(self):
# 2位,不足时以0占位
# return '%d:%d' % (self.hr,self.min)
return '%02d:%02d' % (self.hr,self.min)
# def __repr__(self):
# return repr('%02d:%02d' % (self.hr, self.min))
__repr__= __str__
def __add__(self, other):
# 一般父类使用self.__class__去调用它子类的方法和属性.
# return self.__class__(self.hr+other.hr,self.min+other.min)
hr = self.hr + other.hr
min = self.min + other.min
if min >=60:
min =min-60
hr=hr+1
return self.__class__(hr,min)
def __iadd__(self, other):
self.hr += other.hr
self.min += other.min
if self.min>=60:
self.min =self.min=60
self.hr=self.hr+1
return self
if __name__=='__main__':
ti=Time60(12,5)
print ti
ti1=Time60((10,30),(11,31))
ti2=Time60({'hr':10,'min':40})
ti3=Time60('10:5')
print ti1
print ti2
print ti3
print ti1+ti2
ti1 += ti2
print ti1
ti8=Time60()
print ti8
13-21.装饰符和函数调用语法。第 13.16.4 节末尾,我们使用过一个装饰函数符把 x 转化成一个属性对象,但由于装饰符是 Python2.4 才有的新功能,我们给出了另一个适用于旧版本的语法:
X = property (**x()).
执行这个赋值语句时到底发生了什么呢?为什么它和使用装饰符语句是等价的?
Python 函数装饰器详解:https://www.runoob.com/w3cnote/python-func-decorators.html
装饰器的语法:
@deco
def foo():print 1
foo =deco(foo)
x=property(x)