【python核心编程笔记+习题】-CH13-面向对象编程-下

本文介绍了Python面向对象编程的应用,包括股票投资组合类的实现,支持添加、删除股票代号以及计算年回报率。另外,还展示了如何编写一个DOS风格的命令行接口,模拟Unix命令,如ls、more、cat等。此外,讨论了类的授权概念,以及数值类型、序列类型和映射类型的子类化。最后,提出了改进时间类`Time60`的设计,支持空实例化、格式化输出、不同格式的输入以及六十进制运算功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考链接:

【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)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值