一、写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)