Python cookbook class second
实现一个自定义类来模拟内置的容器类功能
比如列表和字典,但是不确定到底实现哪些方法,collections定义了很多的抽象基类,想让你的类支持迭代,让你的类继承collections.Iterable
import collections
class A(collections.Iterable):
pass
需要实现collections.Iterable所有的抽象方法,否则会报错
import collections
class A(collections.Iterable):
def __iter__(self):
pass
a=A()
import bisect
class SortedItems(collections.Sequence):
def __init__(self,initial=None):
self._items=sorted(initial) if initial is not None else []
def __getitem__(self,index):
return self._items[index]
def __len__(self):
return len(self._items)
def add(self,item):
bisect.insort(self._items,item)
items=SortedItems([5,1,3])
print(list(items))
print(items[0],items[-1])
items.add(2)
print(list(items))
支持所有常规的操作,包括索引,迭代,包含判断,甚至是切片操作
bisect模块,是一个排序列表中插入元素的高效方式,可以保证元素插入后还保持顺序,详细的可以去搜一搜,我已经装载了一篇可以看一看
使用collections中的抽象基类可以确保自定义的容器实现了所有必要的方法,还可以简化类型检查,自定义容器会满足大部分的检查需要
collections中很多抽象类会为常见容器操作提供默认的实现,
class Items(collections.MutableSequence):
def __init__(self,initial):
self._items=initial if initial is not None else []
def __setitem__(self,index,item):#赋值 set
print('set {} into {}'.format(index,item))
self._items[index]=item
def __getitem__(self,index):#get值 get
return self._items[index]
def __delitem__(self,index):#删除 del
del self._items[index]
def insert(self,index,value):#插入,append
self._items.insert(index,value)
def __len__(self):#len
return len(self._items)
numbers模块提供了一个类似的跟整数类型相关的抽象类型集合
15属性的代理访问
问题:想将某个实例的属性访问代理到内部另一个实例中去,目的可能是作为继承的一个替代方法或者实现代理模式
解释:代理是一种编程模式,它将某个操作转移给另外一个对象来实现.
class A:
def spam(self,x):
pass
def foo(self):
pass
class B1:
'''简单的代理'''
def __init__(self):
self._a=A()
def spam(self,x):
return self._a.spam(x)
def foo(self):
return self._a.foo()
def bar(self):
pass
class B2():
"""使用__getattr__的代理,代理方法比较多时候"""
def __init__(self):
self._a=A()
def bar(self):
pass
def __getattr__(self,name):
"""这个方法在访问attribute不存在的时候被调用,
the __getattr__ method is actually a fallback method that only gets
called when an attribute is not found"""
return getattr(self._a,name)
b=B2()
b.bar()
b.spam(42)
#a proxy class that wraps around project,but exposes its public attributes
class Proxy:
def __init__(self,obj):
self._obj=obj
#delegate attributes lookup to iternal obj
def __getattr__(self,name):
print('getattr:',name)
return getattr(self._obj,name)
#delegate attributes assignment
def __setattr__(self,name,value):
if name.startswith('_'):
super().__setattr__(name,value)
else:
print('setattr:',name,value)
setattr(self._obj,name,value)
#delegate attribute deletion
def __delattr__(self,name):
if name.startswith('_'):
super().__delattr__(name)
else:
print('delattr:',name)
delattr(self._obj,name)
class Spam():
def __init__(self,x):
self.x=x
def bar(self,y):
print('Spam.bar:',self.x,y)
#create an instance
s=Spam(2)
p=Proxy(s)
print(p.x)
p.bar(3)
p.x=37
代理类有时候可以作为继承的替代方案.
__getattr__()对于大部分以下划线(__)开始和结尾的属性并不适用
对于列表来说,实现的类并不能实现len和索引元素查找,基本的append和insert是可以实现的,要在类里面实现 __len__,反射机制__getattr__,__setattr__,__delattr__等方法
16在类中定义多个构造器
实现一个类,除了使用__init__方法外,还有其他方式可以初始化它
**解决方案:为了实现多个构造器,需要使用到类方法
import time
class Date:
"""方法一:实用类方法"""
#primary constructor
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@classmethod
def today(cls):
t=time.localtime()
return cls(t.tm_year,t.tm_mon,t.tm_mday)
a=Date(2012,12,21)
b=Date.today()
print(a,b)
class NewDate(Date):
pass
n=NewDate(2012,12,21)
print(n.today)
类方法的一个主要用途就是定义多个构造器,接受一个class作为第一个参数cls,在继承时也能做的很好
类方法的主要用途是:定义构造器
类方法
使用装饰器@classmethod。
原则上,类方法是将类本身作为对象进行操作的方法。假设有个方法,且这个方法在逻辑上采用类本身作为对象来调用更合理,那么这个方法就可以定义为类方法。另外,如果需要继承,也可以定义为类方法。
如下场景:
假设我有一个学生类和一个班级类,想要实现的功能为:
执行班级人数增加的操作、获得班级的总人数;
学生类继承自班级类,每实例化一个学生,班级人数都能增加;
最后,我想定义一些学生,获得班级中的总人数。
思考:这个问题用类方法做比较合适,为什么?因为我实例化的是学生,但是如果我从学生这一个实例中获得班级总人数,在逻辑上显然是不合理的。同时,如果想要获得班级总人数,如果生成一个班级的实例也是没有必要的。
class ClassTest(object):
__num = 0
@classmethod
def addNum(cls):
cls.__num += 1
@classmethod
def getNum(cls):
return cls.__num
# 这里我用到魔术函数__new__,主要是为了在创建实例的时候调用人数累加的函数。
def __new__(self):
ClassTest.addNum()
return super(ClassTest, self).__new__(self)
class Student(ClassTest):
def __init__(self):
self.name = ''
a = Student()
b = Student()
print(ClassTest.getNum())
17创建实例不使用__init__方法,那就使用__new__方法
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
d=Date.__new__(Date)
data={'year':2012,'month':8,'day':29}
for key,value in data.items():
setattr(d,key,value)
print(d.year,d.day)
#想将一个字典转化为date类型
from time import localtime
class Date:
#primary constructor
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@classmethod
def today(cls):
d=cls.__new__(cls)
t=time.localtime()
d.year=t.tm_year
d.month=t.tm_mon
d.day=t.tm_mday
return d
__init__和__new__区别:
继承自object的新式类才有__new__
__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例
__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行
class A(object):
pass
class B(A):
def __init__(self):
print ("init")
def __new__(cls,*args, **kwargs):
print ("new %s"%cls)
return object.__new__(A, *args, **kwargs)
b=B()
print (type(b))
19实现状态对象或者状态机
不同状况下执行那个操作的对象,但是又不想在代码里面出现太多的条件判断语句
解决办法:有些对象会根据状态不同执行不同的操作,比如考虑如下的一个连接对象
class ClosedConnectionState(ConnectionState):
@staticmethod
def write(conn):
raise RuntimeError('Not Open')
@staticmethod
def open(conn):
conn.new_state(OpenConnectionState)
@staticmethod
def close(conn):
raise RuntimeError('Already closed')
class Connection:
def __init__(self):
self.new_state(ClosedConnetionState)
def new_state(self,newstate):
self._state=newstate
def read(self):
return self.__state.read(self)
def write(self):
return self._state.write(self,data)
def open(self):
return self._state.open(self)
def close(self):
return self._state.close(self)
#Connection state base class
class ConnectionState:
@staticmethod
def read(conn):
raise NotImplementedError()
def write(conn,data):
raise NotImplementedError()
def open(conn):
raise NotImplementedError()
def close(conn):
raise NotImplementedError()
class OpenConnectionState(ConnectionState):
@staticmethod
def read(conn):
print('reading')
@staticmethod
def write(conn,data):
print('writing')
@staticmethod
def open(conn):
raise RuntimeError('Already open')
@staticmethod
def close(conn):
conn.new_state(ClosedConnectionState)
c=Connection()
c._state
#状态模式的初级,设计模式的一种
20通过字符串调用对象方法
有一个字符串的方法全称,想通过它调用某个对象的对应方法
解决方法:使用getattr()
import math
class Point:
def __init__(self,x,y):
self.x=x
self.y=y
def __repr__(self):
return "Point({!r},{!r})".format(self.x,self.y)
def distance(self,x,y):
return math.hypot(self.x-x,self.y-y)
p=Point(2,3)
d=getattr(p,'distance')(0,0) #Calls p.distance(0,0)
print(d)
import operator
operator.methodcaller('distance',0,0)(p)
21 实现访问者模式
处理由大量不同类型的对象组成的复杂数据结构,每一个对象都需要不同的处理
class NodeVisitor:
def visit(self,node):
methname='visit_'+type(node).__name__
meth=getattr(self,methname,None)
if meth is None:
meth=self.generic_visit
return meth(node)
def generic_visit(self,node):
raise RuntimeError('No {} method '.format('visit_'+type(node).__name))
class Evaluator(NodeVisitor):
def visit_Number(self,node):
return node.value
def visit_Add(self,node):
return self.visit(node.left)+self.visit(node.right)
def visit_Sub(self,node):
return self.visit(node.left)-self.visit(node.right)
def visit_Mul(self,node):
return self.visit(node.left)*self.visit(node.right)
def visit_Div(self,node):
return self.visit(node.left)/self.visit(node.right)
def visit_Negate(self,node):
return -node.operand
e=Evaluator()
e.visit(t4)