Week 8: 面向对象习题、python实现链表和魔术方法

本文详细介绍了如何使用Python实现链表,包括单向链表和双向链表,并实现了相关操作方法。接着探讨了魔术方法的应用,通过容器化改写习题,如模拟购物车和斐波那契数列。最后讲解了魔术方法中的上下文管理,展示了如何统计函数运行时长并使用@TimeIt装饰器。
摘要由CSDN通过智能技术生成

一、写shape类求面积

在这里插入图片描述

import math
import json
import msgpack


class Shape:
    @property   
    def area(self):
        raise NotImplementedError('基类未实现')


class Triangle(Shape):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c


    @property
    def area(self):
        p = (self.a + self.b + self.c) / 2
        return math.sqrt(p * (p - self.a) * (p - self.b) * (p - self.c))


class Squareness(Shape):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def area(self):
        return self.a * self.b


class Circle(Shape):
    def __init__(self, r):
        self.r = r
    
    @property
    def area(self):
        return 3.14 * self.r * self.r


shapes = [Triangle(3, 4, 5), Squareness(3, 4), Circle(4)]
for s in shapes:
    print('the area of {} is {}'.format(s.__class__.__name__,s.area))


class SerializableMixin:    #用mixin方式实现序列化,也可以使用装饰器,目前支持json和msgpack的序列化
    def dumps(self, t='json'):
        if t == 'json':
            return json.dumps(self.__dict__)
        elif t == 'msgpack':
            return msgpack.dumps(self.__dict__)
        else:
            raise NotImplementedError('未实现序列化')


class SerializableCircleMixin(SerializableMixin, Circle): 
    pass

scm = SerializableCircleMixin(4)
print(scm.area)
s1 = scm.dumps('json')
s2 = scm.dumps('msgpack')
print(s1,s2)

#运行结果:
the area of Triangle is 6.0
the area of Squareness is 12
the area of Circle is 50.24
50.24
{"r": 4} b'\x81\xa1r\x04'

也可以不使用属性装饰器property,写成方法调用也行,即:

shapes = [Triangle(3, 4, 5), Squareness(3, 4), Circle(4)]
for s in shapes:
    print('the area of {} is {}'.format(s.__class__.__name__,s.area()))

二、python实现链表

在这里插入图片描述

1、单向链表
实现append、iternodes方法

class Node:  # 节点类
    def __init__(self, value):
        self.value = value
        self.next = None

    def setvalue(self, newvalue):
        self.value = newvalue

    def setnext(self, newnext):
        self.next = newnext


class SingleLinkedList:
    def __init__(self):
        self.head = None

    def append(self, item):
        temp = Node(item)
        if self.head == None:
            self.head = temp
        else:
            current = self.head
            while current.next != None:
                current = current.next
            current.setnext(temp)

    def iternodes(self):
        current = self.head
        while current.next != None:
            temp = current.value
            current = current.next
            yield temp
        else:
            yield current.value

a = SingleLinkedList()
a.append(5)
a.append(7)
a.append(3)
a.append(4)

for i in a.iternodes():
    print(i)


#运行结果:
5
7
3
4
# By Teacher Wayne

class Node:
    def __init__(self,item,next=None):
        self.item=item
        self.next=next

    def __repr__(self):
        return '< Node {} --> {} >'.format(self.item,self.next.item if self.next else 'None')

class SingleLinkedList:
    # 头和尾均保存节点,注意每次修改过程中都需要改头尾
    # 设立头尾这样的属性能解决头部/尾部追加修改等操作,而且非常方便
    def __init__(self):
        self.head=None
        self.tail=None

    def append(self,item):
        node=Node(item) # 一个元素对象
        if self.head is None:  # 里面没有元素
            self.head=node
        else:
            self.tail.next=node
        self.tail=node
        return self

    def iternodes(self):
        current=self.head
        while current:
            yield current
            current=current.next

l=SingleLinkedList()
l.append(7).append(5).append(3).append(4)
for i in l.iternodes():
    print(i)

2、双向链表
实现append、pop、insert、remove、iternodes方法

class Node:  # 节点类
    def __init__(self, value):
        self.value = value
        self.next = None
        self.prev = None

    def setvalue(self, newvalue):
        self.value = newvalue

    def setnext(self, newnext):
        self.next = newnext

    def setprev(self, newprev):
        self.prev = newprev


class DoubleLinkedList:
    def __init__(self):
        self.head = None

    def getlength(self):
        current = self.head
        length = 1
        while current.next != None:
            length += 1
            current = current.next
        return length

    def append(self, item):
        temp = Node(item)
        if self.head == None:
            self.head = temp
        else:
            current = self.head
            while current.next != None:
                previous = current
                current = current.next
                current.prev = previous
            current.setnext(temp)

    def pop(self):
        pre = self.head
        current = pre.next
        while current.next != None:
            pre = current
            current = current.next
        else:
            pre.next = None

    def insert(self, position, item): 
        length = self.getlength()
        if position <= 1:
            temp = Node(item)
            temp.setnext(self.head)
            self.head = temp
        elif position <= length:
            current = self.head
            pre = None
            temp = Node(item)
            count = 1
            while count < position:
                count += 1
                pre = current
                current = current.next
            pre.setnext(temp)
            temp.setprev(pre)
            temp.setnext(current)
            current.setprev(temp)
        else:
            self.append(item)

    def remove(self, item):
        current = self.head
        pre = None
        while current != None:
            if current.value == item:
                if not pre:  # 如果移除的正好是head第一个
                    self.head = current.next
                    current.next.prev = None
                else:
                    pre.setnext(current.next)
                    current.next.setprev(pre)
                break
            else:
                pre = current
                current = current.next

    def iternodes(self):
        current = self.head
        while current.next != None:
            yield current.value
            current = current.next
        while current.prev != None:
            yield current.value
            current = current.prev
        else:
            yield current.value


a = DoubleLinkedList()
a.append(5)
a.append(7)
a.append(3)
a.append(4)
a.append(9)
a.insert(2, 6)  # 空head作为索引0,有值的为索引1
a.remove(3)
a.pop()
print("length is {}".format(a.getlength()))

for i in a.iternodes():
    print(i)

#运行结果:
5
6
7
4
7
6
5
# By Teacher Wayne

class Node:
    def __init__(self,item,next=None,prev=None):
        self.item=item
        self.next=next
        self.prev=prev

    def __repr__(self):
        return '< Node {} <-- {} --> {} >'.format(self.prev.item if self.prev else 'None',self.item,self.next.item if self.next else 'None')

class DoubleLinkedList:
    # 头和尾均保存节点,注意每次修改过程中都需要改头尾
    # 设立头尾这样的属性能解决头部/尾部追加修改等操作,而且非常方便
    def __init__(self):
        self.head=None
        self.tail=None

    def append(self,item):
        node=Node(item) # 一个元素对象
        if self.head is None:  # 里面没有元素
            self.head=node
        else:
            node.prev=self.tail
            self.tail.next=node
        self.tail=node
        return self

    def insert(self,index,value):
        if index<0:
            raise IndexError('Negative index error {}'.format(index)) 
        for i,node in enumerate(self.iternodes()):
            if i==index:
                current=node
                break
        else:
            self.append(value)
            return
        # 找到了插入点和元素,且一定非空(至少有一个元素)
        node=Node(value) # 待加入节点对象
        if index==0: # 如果在第一个插入
            self.head=node          
        else:
            node.prev=current.prev
            current.prev.next=node
        node.next=current
        current.prev=node

    def pop(self):
        if self.tail is None:
            raise Exception('Empty')
        node=self.tail
        if node.prev is None: #only one node
            self.head=None  # 清空头尾即可
            self.tail=None
        else:
            node.prev.next=None
            self.tail=node.prev
        return node.item

    def remove(self,index):
        if index<0:
            raise IndexError('Negative index error {}'.format(index))
        if self.head is None: # 全空
            raise Exception('Empty')
        for i,node in enumerate(self.iternodes()):
            if i==index:
                current=node
                break
        else:
            raise IndexError('Wrong index')
        # 分四种情况,一种只有一个元素,另三种分头、中、尾
        if current.prev is None and current.next is None:
            self.head=None
            self.tail=None
        elif current.prev is None:
            self.head=current.next
            current.next.prev=None
        elif current.next is None:
            self.tail=current.prev
            current.prev.next=None
        else:
            current.prev.next=current.next
            current.next.prev=current.prev

    def iternodes(self,reverse=False):
        current=self.tail if reverse else self.head
        while current:
            yield current
            current=current.next if not reverse else current.prev

l=DoubleLinkedList()
l.append(7).append(5).append(3).append(9).append(4)
l.pop()
l.insert(0,2) # 第一个值索引为0
l.insert(2,6)
l.remove(3)

for i in l.iternodes():
    print(i)
print()
for i in l.iternodes(reverse=True):
    print(i)

#运行结果:
< Node None <-- 2 --> 7 >
< Node 2 <-- 7 --> 6 >
< Node 7 <-- 6 --> 3 >
< Node 6 <-- 3 --> 9 >
< Node 3 <-- 9 --> None >

< Node 3 <-- 9 --> None >
< Node 6 <-- 3 --> 9 >
< Node 7 <-- 6 --> 3 >
< Node 2 <-- 7 --> 6 >
< Node None <-- 2 --> 7 >

3、增加__getitem__,__setitem__等方法

class Node:
    def __init__(self,item,next=None,prev=None):
        self.item=item
        self.next=next
        self.prev=prev

    def __repr__(self):
        return '< Node {} <-- {} --> {} >'.format(self.prev.item if self.prev else 'None',self.item,self.next.item if self.next else 'None')

class DoubleLinkedList:
    # 头和尾均保存节点,注意每次修改过程中都需要改头尾
    # 设立头尾这样的属性能解决头部/尾部追加修改等操作,而且非常方便
    def __init__(self):
        self.head=None
        self.tail=None
        self.length=0

    def __len__(self):
        return self.length

    def append(self,item):
        node=Node(item) # 一个元素对象
        if self.head is None:  # 里面没有元素
            self.head=node
        else:
            node.prev=self.tail
            self.tail.next=node
        self.tail=node
        self.length+=1
        return self

    def insert(self,index,value):
        if index<0:
            raise IndexError('Negative index error {}'.format(index)) 
        for i,node in enumerate(self.iternodes()):
            if i==index:
                current=node
                break
        else:
            self.append(value)
            return
        # 找到了插入点和元素,且一定非空(至少有一个元素)
        node=Node(value) # 待加入节点对象
        if index==0: # 如果在第一个插入
            self.head=node          
        else:
            node.prev=current.prev
            current.prev.next=node
        node.next=current
        current.prev=node
        self.length+=1

    def pop(self):
        if self.tail is None:
            raise Exception('Empty')
        node=self.tail
        if node.prev is None: #only one node
            self.head=None  # 清空头尾即可
            self.tail=None
        else:
            node.prev.next=None
            self.tail=node.prev
        self.length-=1
        return node.item

    def remove(self,index):
        if index<0:
            raise IndexError('Negative index error {}'.format(index))
        if self.head is None: # 全空
            raise Exception('Empty')
        for i,node in enumerate(self.iternodes()):
            if i==index:
                current=node
                break
        else:
            raise IndexError('Wrong index')
        # 分四种情况,一种只有一个元素,另三种分头、中、尾
        if current.prev is None and current.next is None:
            self.head=None
            self.tail=None
        elif current.prev is None:
            self.head=current.next
            current.next.prev=None
        elif current.next is None:
            self.tail=current.prev
            current.prev.next=None
        else:
            current.prev.next=current.next
            current.next.prev=current.prev
        self.length-=1

    def iternodes(self,reverse=False):
        current=self.tail if reverse else self.head
        while current:
            yield current
            current=current.next if not reverse else current.prev

    def __getitem__(self,index):
        flag=False if index>0 else True
        start=0 if index>0 else 1
        for i,node in enumerate(self.iternodes(flag),start):
            if i==abs(index):
                return node
            else:
                raise IndexError

    def __setitem__(self,key,value):
        self[key].item=value

    def __iter__(self):
        return self.iternodes()

l=DoubleLinkedList()
l.append(7).append(5).append(3).append(9).append(4)
l.pop()
l.insert(0,2) # 第一个值索引为0
l.insert(2,6)
l.remove(3)

for i in l.iternodes():
    print(i)
print()
for i in l.iternodes(reverse=True):
    print(i)
print()

print(l[2])
print(l[-2])
l[3]=7
for i in l.iternodes():
    print(i)

三、魔术方法-用容器化改写习题

想对某些对象进行容器化操作时,可封装起来添加魔术方法

1、模拟购物车购物

class Cart:
    def __init__(self):
        self.__items=[]

    def additem(self,item):
        self.__items.append(item)

    def __len__(self):  # 实现取得商品个数
        return len(self.__items)

    def __iter__(self):  # 实现直接迭代一个实例对象
        return iter(self.__items)
        # 或者 yield from self.__items

    def __getitem__(self,index): # 实现从索引或key取第几项元素
        return self.__items[index]

    def __setitem__(self,index,value):  # 实现修改某索引的值
        self.__items[index]=value

    # 实现不断往购物车添加物品
    # 想实现 cart+4,做运算符重载加入元素,即链式编程
    def __add__(self,other):
        self.__items.append(other)
        return self  # 实现连续加 cart.__add__(3).__add__(4),即 cart+3+4

2、改写斐波那契数列

class Fib:
    def __init__(self):
        # 使用缓存,减少计算
        self.items=[0,1,1]

    def __call__(self,index):
        if index>=len(self.items):
            for i in range(len(self)+1,index+1):
                self.items.append(self.items[i-1]+self.items[i-2])
        return self.items[index]

    def __getitem__(self,index):
        return self(index)

    # 获取长度
    def __len__(self):
        return len(self.items)-1

    # 解决迭代问题,把fib对象当可迭代对象
    def __iter__(self):
        return iter(self.items)


fib=Fib()
# 虽然内部都是用call方法实现,但调用的意义完全不同
print(fib(5))  # 把实例当可调用实例来看
print(fib[5])  # 把实例当可索引的list或dict来用

for x in fib:
    print(x)

四、魔术方法-上下文管理

统计函数的运行时长

#  普通的函数装饰器

import datetime
import time
from functools import wraps,update_wrapper

def logger(fn):
    @wraps(fn)  #wrapper=wraps(fn)(wrapper)
    def wrapper(*args,**kwargs):
        start=datetime.datetime.now()
        ret=fn(*args,**kwargs)
        delta=(datetime.datetime.now()-start).total_seconds()
        print("{} took {}s.".format(fn.__name__,delta))
        return ret
    return wrapper

@logger  # add=logger instance
def add(x=4,y=5):
    """this is a add function"""
    time.sleep(1)
    return x+y

print(add())
print(add.__doc__)
# 上下文管理1:

from functools import wraps

class TimeIt:
    def __init__(self,fn):
        self.fn=fn

    def __enter__(self):
        self.start=datetime.datetime.now()
        return self

    def __exit__(self,exc_type,exc_val,exc_tb):
        self.delta=(datetime.datetime.now()-self.start).total_seconds()
        print("{} took {}s.".format(self.fn.__name__,self.delta))

def add(x=4,y=5):
    """this is a add function"""
    time.sleep(1)
    return x+y

with TimeIt(add):
    print(add())
print(add.__doc__)
#  上下文管理2:

import datetime
import time

class TimeIt:
    def __init__(self,fn):
        self.fn=fn

    def __enter__(self):
        self.start=datetime.datetime.now()
        return self

    def __exit__(self,exc_type,exc_val,exc_tb):
        self.delta=(datetime.datetime.now()-self.start).total_seconds()
        print("{} took {}s.".format(self.fn.__name__,self.delta))

    def __call__(self,*args,**kwargs):
        return self.fn(*args,**kwargs)

def add(x=4,y=5):
    """this is a add function"""
    time.sleep(1)
    return x+y

with TimeIt(add) as f:
    print(f(3,4))
#  把一个类当作装饰器

import datetime
import time

class TimeIt:
    def __init__(self,fn):
        self.fn=fn

    def __call__(self,*args,**kwargs):
        self.start=datetime.datetime.now()
        ret=self.fn(*args,**kwargs)
        self.delta=(datetime.datetime.now()-self.start).total_seconds()
        print("{} took {}s.".format(self.fn.__name__,self.delta))
        return ret

# 通过调用call方法知道,传进了self
@TimeIt  # add=TimeIt(add) 右边先运行,add就是TimeIt的一个实例
def add(x=4,y=5):
    time.sleep(1)
    return x+y

print(add(3,4)) # add=TimeIt instance 该实例可调用

@TimeIt
注意等价式:add=TimeIt(add)
右边先运行,add变成了TimeIt的一个实例,该实例可调用(call

接下来处理把__doc__固定为原函数的属性

# 核心等价式 wraps(fn)(self)=update_wrapper(self,fn),其中wraps(fn)是偏函数,返回一个新函数(缺1个参数),调用目标self后,即把self的部分属性修改为原函数fn的

import datetime
import time
from functools import wraps,update_wrapper


class TimeIt:
    def __init__(self,fn):
        self.fn=fn
        # self=wrapper fn=wrapped self的__name__,__doc__需要被fn修改
        update_wrapper(self,fn)

    def __call__(self,*args,**kwargs):
        self.start=datetime.datetime.now()
        ret=self.fn(*args,**kwargs)
        self.delta=(datetime.datetime.now()-self.start).total_seconds()
        print("{} took {}s.".format(self.fn.__name__,self.delta))
        return ret

# 通过调用call方法知道,传进了self
@TimeIt  # add=TimeIt(add) 右边先运行,add就是TimeIt的一个实例
def add(x=4,y=5):
    """this is a add function"""
    time.sleep(1)
    return x+y

print(add(3,4)) # add=TimeIt instance 该实例可调用
print(add.__doc__)

也可以这样写:

import datetime
import time
from functools import wraps,update_wrapper


class TimeIt:
    def __init__(self,fn):
        self.fn=fn
        wraps(fn)(self)

    def __call__(self,*args,**kwargs):
        self.start=datetime.datetime.now()
        ret=self.fn(*args,**kwargs)
        self.delta=(datetime.datetime.now()-self.start).total_seconds()
        print("{} took {}s.".format(self.fn.__name__,self.delta))
        return ret

# 通过调用call方法知道,传进了self
@TimeIt  # add=TimeIt(add) 右边先运行,add就是TimeIt的一个实例
def add(x=4,y=5):
    """this is a add function"""
    time.sleep(1)
    return x+y

print(add(3,4)) # add=TimeIt instance 该实例可调用
print(add.__doc__)

还可以用context manager,装饰一个普通函数使之具有上下文管理机制

import datetime
import time
from contextlib import contextmanager

@contextmanager
def add(x,y):
    start=datetime.datetime.now()
    try:
        yield x+y
    finally:
        delta=(datetime.datetime.now()-start).total_seconds()
        print("add took {}s.".format(delta))

with add(4,5) as f:
    time.sleep(1)
    print(f)
python023基于Python旅游景点推荐系统带vue前后端分离毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值