Python cookbook class second

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)

猜你喜欢

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