3想让对象支持上下文的with函数,必须支持__enter__和__exit__函数??
from socket import socket,AF_INET,SOCK_STREAM
class LazyConnection:def __init__(self,address,family=AF_INET,type=SOCK_STREAM):self.address=addressself.family=familyself.type=typeself.sock=Nonedef __enter__(self):if self.sock is not None:raise RutimeError('Already connected')self.sock=socket(self.family,self.type)self.sock.connect(self.address)return self.sockdef __exit__(self,exc_ty,exc_val,tb):self.sock.close()self.sock=None
from functools import partial
conn=LazyConnection(('www.python.com',80))
with conn as s:s.send(b'GET/index.html HTTP/1.0\r\n')s.send(b'Host:www.python.org\r\n')s.send(b'\r\n')resp=b''.join(iter(partial(s.recv,8192),b''))
6创建可管理的属性
attribute增加除访问与修改之外的其他处理逻辑,比如类型检查或合法性验证
class Person:def __init__(self,first_name):self.first_name=first_name#Getter function@propertydef first_name(self):return self._first_name@first_name.setterdef first_name(self,value):if not isinstance(value,str):raise TypeError('Expected a string')self._first_name=value#delete function@first_name.deleterdef first_name(self):raise AttributeError("Can't delete attribute")
第一个方法是一个getter函数,它使得first_name成为一个属性,其他两个方法给first_name属性添加了setter和deleter函数,需要强调的是只有在first_name属性被创建了之后,后面的两个装饰器@first_name.setter和@first_name.deleter才能被定义
第一个first_name会自动属性检查,而_first_name属性的操作是在调用了property才会被触发,property实现的时候,底层数据仍然需要存储在某个地方,而且一个property属性其实就是一系列相关绑定方法的集合,如果查看拥有property的类,就会发现property本身的fget,fset,fdel属性就是类里面的普通方法
不要写property属性和setter相同的方法.Properties还是一种定义动态计算attribute的方法,不会做实际的存储,而是在需要的时候计算出来
不要进行大段的有重复性的property定义...
7调用父类已经被覆盖的方法
super()函数 super.__init__()会保证每个__init__()方法只会被调用一次
方法解析顺序列表MRO
子类会先于父类被检查
多个父类会根据它们在列表中的顺序被检查
如果对下一个类存在两个合法的选择,选择第一个父类
class SubPerson(Person):@Person.first_name.getterdef name(self):print('Geeting name')return super().first_name
9创建新的实例或者方法
class Integer:def __init__(self,name):self.name=namedef __get__(self,instance,cls):if instance is None:return selfelse:return instance.__dict__[self.name]def __set__(self,instance,value):if not isinstance(instance,str):raise TypeError('Expected an int')instance.__dict__[self.name]=valuedef __delete__(self,instance):del instance.__dict__[self.name]#一个描述器类就是实现了三个核心的属性访问操作get setdelete的类,分别为__get__() __set__()和__delete__这三个特殊的方法,这些方法#接受一个实例作为输入,之后相应的操作实例底层的字典
class Point():def __init__(self,x,y):self.x=xself.y=y
10使用延迟计算属性
class lazyproperty:def __init__(self,func):self.func=funcdef __get__(self,instance,cls):if instance is None:return selfelse:value=self.func(instance)setattr(instance,self.func.__name__,value)return value
import math
class Circle:def __init__(self,radius):self.radius=radius@lazypropertydef area(self):print('Computing area')return math.pi*self.radius**2@lazypropertydef perimeter(self):print('Computing perimeter')return 2*math.pi*self.radius
c=Circle(4.0)
c.radius
只有第一次运行的时候print了,然后就将内容存储到了内存中,其中类的属性是可以更改的,可以通过更改描述器使其属性不被爆漏
def lazyproperty(func):name='_lazy_'+func.__name__@propertydef lazy(self):if hasattr(self,name):return getattr(self,name)else:value=func(self)setattr(self,name,value)return valuereturn lazy
11简化数据结构的初始化
在一个基类写一个公用的__init__()函数
class Structure:_fields=[]def __init__(self,*args):if len(args) != len(self._fields):raise TypeError('Expected {} arguements'.format(len(self._fields)))#set the argumentsfor name, value in zip(self._fields,args):setattr(self,name,value)class Stock(Structure):_fields = ['name','shares','prices']
s = Stock('ACER',50,99)
print(s.name,s.shares,s.prices)class Point(Structure):_fields = ['x','y']
p = Point(4,5)
print(p.x,p.y)#需要额外添加参数
class Structure:_fields=[]def __init__(self,*args,**kwargs):if len(args) != len(self._fields):raise TypeError('Expected {} arguements'.format(len(self._fields)))#set the argumentsfor name, value in zip(self._fields,args):setattr(self,name,value)#set The additional argument(if any):extra_args=kwargs.keys()-self._fieldsfor name in extra_args:setattr(self,name,kwargs.pop(name))if kwargs:raise TypeError('Duplicate values for {}'.format(','.join(kwargs)))
class Stock(Structure):_fields=['name','shares','price']
s1=Stock('ACME',50,91.1)
s2=Stock("ACME",50,91.1,date='8/2/2012')
print(s1,s2)
12定义接口或抽象基类
定义一个接口或抽象类,并且通过执行类型检查来确保子类实现了某些特定的方法
abc模块???这个是啥???
from abc import ABCMeta,abstractmethod
class IStream(metaclass=ABCMeta):@abstractmethoddef read(self,maxbytes=-1):pass@abstractmethoddef write(self,data):pass
class SocketStream(IStream):def read(self,maxbytes=-1):passdef write(self,data):pass
def serialize(obj,stream):if not isinstance(stream,IStream):raise TypeError('Expected an IStream')pass
s=SocketStream()
serialize(s,s)
import io
IStream.register(io.IOBase)
f=open('cookie.txt')
isinstance(f,IStream)
抽象类不能被实例化,抽象类的目的是让别的类继承它并实现特定的抽象方法,还可以通过注册方式让某个类是吸纳抽象基类
@abstractmethod 还能注解静态方法类方法和properties,要在紧挨着函数前定义
标准库中有很多用到了抽象基类的地方,collections模块定义了很多跟容器和迭代器(序列,映射,集合等)有关的抽象基类,numbers库定义了跟数字对象(整数,浮点数,有理数等)有关的基类,io库定义了很多跟I/O操作相关的基类
13实现数据模型的类型约束
定义某些在属性赋值上面有限制的数据结构
解决办法:对某些实例属性赋值时进行检查,自定义属性赋值函数,使用描述器
系统类型和赋值框架
# Base class. Uses a descriptor to set a value
class Descriptor:def __init__(self, name=None, **opts):self.name = namefor key, value in opts.items():setattr(self, key, value)def __set__(self, instance, value):instance.__dict__[self.name] = value# Descriptor for enforcing types
class Typed(Descriptor):expected_type = type(None)def __set__(self, instance, value):if not isinstance(value, self.expected_type):raise TypeError('expected ' + str(self.expected_type))super().__set__(instance, value)# Descriptor for enforcing values
class Unsigned(Descriptor):def __set__(self, instance, value):if value < 0:raise ValueError('Expected >= 0')super().__set__(instance, value)class MaxSized(Descriptor):def __init__(self, name=None, **opts):if 'size' not in opts:raise TypeError('missing size option')super().__init__(name, **opts)def __set__(self, instance, value):if len(value) >= self.size:raise ValueError('size must be < ' + str(self.size))super().__set__(instance, value)class Integer(Typed):expected_type = intclass UnsignedInteger(Integer, Unsigned):passclass Float(Typed):expected_type = floatclass UnsignedFloat(Float, Unsigned):passclass String(Typed):expected_type = strclass SizedString(String, MaxSized):passclass Stock:# Specify constraintsname = SizedString('name', size=8)shares = UnsignedInteger('shares')price = UnsignedFloat('price')def __init__(self, name, shares, price):self.name = nameself.shares = sharesself.price = price
类装饰器
# Class decorator to apply constraints
def check_attributes(**kwargs):def decorate(cls):for key, value in kwargs.items():if isinstance(value, Descriptor):value.name = keysetattr(cls, key, value)else:setattr(cls, key, value(key))return clsreturn decorate# Example
@check_attributes(name=SizedString(size=8),shares=UnsignedInteger,price=UnsignedFloat)
class Stock:def __init__(self, name, shares, price):self.name = nameself.shares = sharesself.price = price
使用元类:
# A metaclass that applies checking
class checkedmeta(type):def __new__(cls, clsname, bases, methods):# Attach attribute names to the descriptorsfor key, value in methods.items():if isinstance(value, Descriptor):value.name = keyreturn type.__new__(cls, clsname, bases, methods)# Example
class Stock2(metaclass=checkedmeta):name = SizedString(size=8)shares = UnsignedInteger()price = UnsignedFloat()def __init__(self, name, shares, price):self.name = nameself.shares = sharesself.price = price
使用装饰器
# Decorator for applying type checking
def Typed(expected_type, cls=None):if cls is None:return lambda cls: Typed(expected_type, cls)super_set = cls.__set__def __set__(self, instance, value):if not isinstance(value, expected_type):raise TypeError('expected ' + str(expected_type))super_set(self, instance, value)cls.__set__ = __set__return cls# Decorator for unsigned values
def Unsigned(cls):super_set = cls.__set__def __set__(self, instance, value):if value < 0:raise ValueError('Expected >= 0')super_set(self, instance, value)cls.__set__ = __set__return cls# Decorator for allowing sized values
def MaxSized(cls):super_init = cls.__init__def __init__(self, name=None, **opts):if 'size' not in opts:raise TypeError('missing size option')super_init(self, name, **opts)cls.__init__ = __init__super_set = cls.__set__def __set__(self, instance, value):if len(value) >= self.size:raise ValueError('size must be < ' + str(self.size))super_set(self, instance, value)cls.__set__ = __set__return cls# Specialized descriptors
@Typed(int)
class Integer(Descriptor):pass@Unsigned
class UnsignedInteger(Integer):pass@Typed(float)
class Float(Descriptor):pass@Unsigned
class UnsignedFloat(Float):pass@Typed(str)
class String(Descriptor):pass@MaxSized
class SizedString(String):pass