python cookbook class

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

猜你喜欢

转载自blog.csdn.net/qq_37312720/article/details/84765723