目录
8.15委托属性的访问
8.16在类中定义多个构造函数
8.17 不通过调用init来创建实例
8.18用Mixin类来扩展类定义
8.19实现带有状态的对象和状态机
8.20 调用对象上的方法,方法名以字符串形式给出
8.24让类支持比较操作
8.25 创建缓存实例
8.15委托属性的访问
# 8.15委托属性的访问
# 委托是一种编程模式,我们将某个特定的操作转交另一个不同的额对象实现,简单来说就是这样的
class A:
def spam(self,x):
pass
def foo(self):
pass
class B:
def __init__(self):
self._a = A()
def sapm(self,x):
return self._a.spam(x)
def foo(self):
return self._a.foo()
#实现代理的一个例子
class Proxy:
def __init__(self,obj):
self._obj = obj
#委托属性浏览内部属性
def __getattr__(self, name):
print('Getting:',name)
return getattr(self._obj,name)
#委托属性设置
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name,value)
else:
print('setattr:',name,value)
setattr(self._obj,name,value)
#委托属性删除
def __delete__(self, name):
if name.startswith('_'):
super().__delattr__(name)
else:
print('Deleting:',name)
delattr(self._obj,name)
#使用这个代理类
class Spam:
def __init__(self,x):
self.x = x
def bar(self,y):
print('Spam.bar:',self.x,y)
#创建一个实例
a = Spam(2)
#创建一个委托
s = Proxy(a)
print(s.x)
# Getting: x
# 2
s.bar(3)
# Getting: bar
# Spam.bar: 2 3
s.x = 37
# set
8.16在类中定义多个构造函数
# 用户不限于__init__()提供的方法来创建实例
import time
class Date:
#第一种
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(2022,5,16)
b = Date.today()
8.17 不通过调用init来创建实例
class Date:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
d = Date.__new__(Date)
print(d)
d.year = 2022
d.month = 5
d.day = 16
print(d.year,d.month,d.day) #2022 5 16 原文中不能直接设置,可能版本更新,可以直接设置响应的值
data = {'year':2022,'month':5,'day':17} #实现字典的反序列化,将字典转化为实例
for key,value in data.items():
setattr(d,key,value)
print(d.year,d.month,d.day)
8.18用Mixin类来扩展类定义
# 在类进行定制化处理时,我们有兴趣将各种各样的定制化处理方法添加到映射方法中
class LoggeMappingMixin:
#添加一些日志设置检查的功能
__slots__ = ()
def __getitem__(self, key):
print('Getting:' + str(key))
return super().__getitem__(key) #在这里使用super()是必要的,通过这个函数将任务转交给解析顺序(MRO)上的下一个类
def __setattr__(self, key, value):
print('Setting {} = {!r}'.format(key,value))
return super().__setitem__(key,value)
def __delitm__(self,key):
print('Deleting ' + str(key))
return super().__delitem__(key)
class SetOnceMappingMixin:
#设置只允许设置一次键
__slots__ = ()
def __setitem__(self, key, value):
if key in self:
raise KeyError(str(key) + ' already set')
return super().__setitem__(key,value)
class StringKeysMappingMixin:
#限制键必须是字符串类型
__slots__ = ()
def __setitem__(self, key, value):
if not isinstance(key,str):
raise TypeError('keys must be strings')
return super().__setitem__(key,value)
#使用这些类,这些类单独并没有什么用,只有和其他类一起使用时,才能发挥起作用
class LoggeDict(LoggeMappingMixin,dict): #这里调用LoggeMappingMixin中的函数实际上传递给dict了
pass
d = LoggeDict()
d['x'] = 2
print(d['x'])
del d['x']
from collections import defaultdict
class SetOnceDefaultDict(SetOnceMappingMixin,defaultdict):
pass
d = SetOnceDefaultDict(list)
d['x'].append(2)
d['y'].append(3)
d['x'].append(9)
print(d)
d['x'] = 1
# Traceback (most recent call last):
# File "E:\vippython\Python CookBook\第八章 类与对象(下).py", line 156, in <module>
# d['x'] = 1
# File "E:\vippython\Python CookBook\第八章 类与对象(下).py", line 127, in __setitem__
# raise KeyError(str(key) + ' already set')
# KeyError: 'x already set'
# Getting: x
from collections import OrderedDict
class StringOrdereDict(StringKeysMappingMixin,
SetOnceMappingMixin,
OrderedDict):
pass
d = StringOrdereDict()
d['x'] = 23
d[42] = 10
# Traceback (most recent call last):
# File "E:\vippython\Python CookBook\第八章 类与对象(下).py", line 172, in <module>
# d[42] = 10
# File "E:\vippython\Python CookBook\第八章 类与对象(下).py", line 135, in __setitem__
# raise TypeError('keys must be strings')
# TypeError: keys must be strings
8.19实现带有状态的对象和状态机
# 想实现对象在不同状态中进行操作,但是并不希望有大量的判断语句,我们将每一种操作状态以单独的类来定义
class Connection:
def __init__(self):
self.new_state(CLosedConnectionState)
def new_state(self,newstate):
self._state = newstate
#委托状态类
def read(self):
return self._state.read(self)
def write(self,data):
return self._state.write(self,data)
def open(self):
return self._state.open(self)
def close(self):
return self._state.close(self)
#连接状态基类
class ConnectionState:
@staticmethod
def read(conn):
raise NotImplementedError()
@staticmethod
def write(conn,data):
raise NotImplementedError()
@staticmethod
def open(conn):
raise NotImplementedError()
@staticmethod
def close(conn):
raise NotImplementedError()
#设置不同状态
class CLosedConnectionState(ConnectionState):
@staticmethod
def read(conn):
raise RuntimeError('Not open')
@staticmethod
def write(conn,data):
raise RuntimeError('Not open')
@staticmethod
def open(conn):
conn.new_state(OpenConnectionState)
@staticmethod
def close(conn):
raise RuntimeError('ALready closed')
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()
print(c._state) #<class '__main__.CLosedConnectionState'>
# c.read()
# Traceback (most recent call last):
# File "E:\vippython\Python CookBook\第八章 类与对象(下).py", line 257, in <module>
# print(c.read())
# File "E:\vippython\Python CookBook\第八章 类与对象(下).py", line 191, in read
# return self._state.read(self)
# File "E:\vippython\Python CookBook\第八章 类与对象(下).py", line 224, in read
# raise RuntimeError('Not open')
# RuntimeError: Not open
c.open()
c.read() #Reading
print(c._state) #<class '__main__.OpenConnectionState'>
c.close()
print(c._state) #<class '__main__.CLosedConnectionState'>
#另一种实现方法是直接更改__class__属性
class Connection: #这里将连接和连接状态进行合并,随着状态的改变实例也会自己修改自己的类型
def __init__(self):
self.new_state(ClosedConnection)
def new_state(self,newstate):
self.__class__ = newstate
def read(self):
raise NotImplementedError()
def write(self):
raise NotImplementedError()
def open(self):
raise NotImplementedError()
def read(self):
raise NotImplementedError()
class ClosedConnection(Connection):
def read(self):
raise RuntimeError('Not open')
def write(self):
raise RuntimeError('Not open')
def open(self):
self.new_state((OpenConnection))
def close(self):
raise RuntimeError('Already closed')
class OpenConnection(Connection):
def read(self):
print('reading')
def write(self):
print('writing')
def open(self):
raise RuntimeError('Already Open')
def close(self):
self.new_state(ClosedConnection)
#试一下
c = Connection()
print(c) #<__main__.ClosedConnection object at 0x00000226505F1A60>
# c.read()
# Traceback (most recent call last):
# File "E:\vippython\Python CookBook\第八章 类与对象(下).py", line 310, in <module>
# c.read()
# File "E:\vippython\Python CookBook\第八章 类与对象(下).py", line 290, in read
# raise RuntimeError('Not open')
# RuntimeError: Not open
c.open()
print(c) #<__main__.OpenConnection object at 0x00000226505F1A60>
c.read() #reading
8.20 调用对象上的方法,方法名以字符串形式给出
# 对于简单的情况 使用getattr() 调用一个方法,可以使用相应getattr查询相应的属性,将查询的方法
# 当做函数进行使用就可以了,使用operator.methodcall()创建一个可调用对象,通过包含在字符串中的
# 名称来调用函数。
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')(3,2) #1.4142135623730951
print(d)
#另一种方法是使用operator.methodcaller()
import operator
a = operator.methodcaller('distance',3,2)(p)
print(a) #1.4142135623730951
#在环状数据结构中管理内存
# 使用weakref库中提供的弱引用机制
import weakref
class Node:
def __init__(self,value):
self.value = value
self._parent = None
self.children = []
def __repr__(self):
return 'node({!r:})'.format(self.value)
@property
def parent(self):
return self._parent if self._parent is None else self._parent()
@parent.setter
def parent(self,node):
self._parent = weakref.ref(node)
def add_child(self,child):
self.children.append(child)
self.parent = self
root = Node('parent')
print(root)
c1 = Node('child')
print(c1)
root.add_child(c1)
print(c1.parent)
del root
print(c1.parent)
8.24让类支持比较操作
# 在类之间实现比较操作,但是不能编写大量的特殊方法
# 举例子 比较房子大小
from functools import total_ordering
class Room:
def __init__(self,name,length,width):
self.name = name
self.length = length
self.width = width
self.square_feet = self.length * self.width
@total_ordering
class House:
def __init__(self,name,style):
self.name = name
self.style = style
self.rooms = []
@property
def living_space_footage(self):
return sum(r.square_feet for r in self.rooms)
def add_room(self,room):
self.rooms.append(room)
def __str__(self):
return '{} : {} square foot {}'.format(self.name,
self.living_space_footage,
self.style)
def __eq__(self, other): #定义进行比较的方法
return self.living_space_footage == other.living_space_footage
def __lt__(self, other):
return self.living_space_footage < other.living_space_footage
h1 = House('h1','Cape')
h1.add_room(Room('Master Bedroom',14,21))
h1.add_room(Room('Living Room',18,21))
h1.add_room(Room('Kitchen',12,16))
h1.add_room(Room('Office',14,12))
h2 = House('h2','Ranch')
h2.add_room(Room('Master Bedroom',14,21))
h2.add_room(Room('Living Room',18,21))
h2.add_room(Room('Kitchen',12,16))
h3 = House('h3','Split')
h3.add_room(Room('Master Bedroom',14,21))
h3.add_room(Room('Living Room',18,21))
h3.add_room(Room('Kitchen',12,16))
h3.add_room(Room('Office',14,17))
houses = [h1,h2,h3]
print(h1 > h2) # True
print(h1 < h2) # False
print(h1 >= h2) # True
print(max(houses)) # h3 : 1102 square foot Split
#__le__ <= __gt__ > __ge__ >= __ne__ ==
8.25 创建缓存实例
# 想要确保针对某一组输入参数只会有一个类实例存在时使用一个与类本身相分离的工厂函数
class Spam:
def __init__(self,name):
self.name = name
import weakref
_spam_cache = weakref.WeakValueDictionary() #将实例缓存再这个里边,但不再需要时,删除就行
def get_spam(name):
if name not in _spam_cache:
s = Spam(name)
_spam_cache[name] = s
else:
s = _spam_cache[name]
return s
a = get_spam('foo')
b = get_spam('bar')
print(a is b) #False
c = get_spam('foo')
print(a is c) #True