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=address
self.family=family
self.type=type
self.sock=None
def __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.sock
def __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
@property
def first_name(self):
return self._first_name
@first_name.setter
def first_name(self,value):
if not isinstance(value,str):
raise TypeError('Expected a string')
self._first_name=value
#delete function
@first_name.deleter
def 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.getter
def name(self):
print('Geeting name')
return super().first_name
9创建新的实例或者方法
class Integer:
def __init__(self,name):
self.name=name
def __get__(self,instance,cls):
if instance is None:
return self
else:
return instance.__dict__[self.name]
def __set__(self,instance,value):
if not isinstance(instance,str):
raise TypeError('Expected an int')
instance.__dict__[self.name]=value
def __delete__(self,instance):
del instance.__dict__[self.name]
#一个描述器类就是实现了三个核心的属性访问操作get setdelete的类,分别为__get__() __set__()和__delete__这三个特殊的方法,这些方法
#接受一个实例作为输入,之后相应的操作实例底层的字典
class Point():
def __init__(self,x,y):
self.x=x
self.y=y
10使用延迟计算属性
class lazyproperty:
def __init__(self,func):
self.func=func
def __get__(self,instance,cls):
if instance is None:
return self
else:
value=self.func(instance)
setattr(instance,self.func.__name__,value)
return value
import math
class Circle:
def __init__(self,radius):
self.radius=radius
@lazyproperty
def area(self):
print('Computing area')
return math.pi*self.radius**2
@lazyproperty
def 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__
@property
def lazy(self):
if hasattr(self,name):
return getattr(self,name)
else:
value=func(self)
setattr(self,name,value)
return value
return 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 arguments
for 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 arguments
for name, value in zip(self._fields,args):
setattr(self,name,value)
#set The additional argument(if any):
extra_args=kwargs.keys()-self._fields
for 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):
@abstractmethod
def read(self,maxbytes=-1):
pass
@abstractmethod
def write(self,data):
pass
class SocketStream(IStream):
def read(self,maxbytes=-1):
pass
def 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 = name
for 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 = int
class UnsignedInteger(Integer, Unsigned):
pass
class Float(Typed):
expected_type = float
class UnsignedFloat(Float, Unsigned):
pass
class String(Typed):
expected_type = str
class SizedString(String, MaxSized):
pass
class Stock:
# Specify constraints
name = SizedString('name', size=8)
shares = UnsignedInteger('shares')
price = UnsignedFloat('price')
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.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 = key
setattr(cls, key, value)
else:
setattr(cls, key, value(key))
return cls
return decorate
# Example
@check_attributes(name=SizedString(size=8),
shares=UnsignedInteger,
price=UnsignedFloat)
class Stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
使用元类:
# A metaclass that applies checking
class checkedmeta(type):
def __new__(cls, clsname, bases, methods):
# Attach attribute names to the descriptors
for key, value in methods.items():
if isinstance(value, Descriptor):
value.name = key
return 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 = name
self.shares = shares
self.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