Python类与对象

内置方法  说明
 __init__(self,...)  初始化对象,在创建新对象时调用
 __del__(self)  释放对象,在对象被删除之前调用
 __new__(cls,*args,**kwd)  实例的生成操作
 __str__(self)  在使用print语句时被调用
 __getitem__(self,key)  获取序列的索引key对应的值,等价于seq[key]
 __len__(self)  在调用内联函数len()时被调用
 __cmp__(stc,dst)  比较两个对象src和dst
 __getattr__(s,name)  获取属性的值
 __setattr__(s,name,value)  设置属性的值
 __delattr__(s,name)  删除name属性
 __getattribute__()  __getattribute__()功能与__getattr__()类似
 __gt__(self,other)  判断self对象是否大于other对象
 __lt__(slef,other)  判断self对象是否小于other对象
 __ge__(slef,other)  判断self对象是否大于或者等于other对象
 __le__(slef,other)  判断self对象是否小于或者等于other对象
 __eq__(slef,other)  判断self对象是否等于other对象
 __call__(self,*args)  把实例对象作为函数调用

__init__():
__init__方法在类的一个对象被建立时,马上运行。这个方法可以用来对你的对象做一些你希望的初始化。注意,这个名称的开始和结尾都是双下划线。
代码例子:

#!/usr/bin/python
# Filename: class_init.py
class Person:
    def __init__(self, name):
        self.name = name
    def sayHi(self):
        print 'Hello, my name is', self.name

p = Person('Swaroop')
p.sayHi()

输出:
Hello, my name is Swaroop

   说明:__init__方法定义为取一个参数name(以及普通的参数self)。在这个__init__里,我们只是创建一个新的域,也称为name。注意它们是两个不同的变量,尽管它们有相同的名字。点号使我们能够区分它们。最重要的是,我们没有专门调用__init__方法,只是在创建一个类的新实例的时候,把参数包括在圆括号内跟在类名后面,从而传递给__init__方法。这是这种方法的重要之处。现在,我们能够在我们的方法中使用self.name域。这在sayHi方法中得到了验证。

__new__():
__new__()在__init__()之前被调用,用于生成实例对象.利用这个方法和类属性的特性可以实现设计模式中的单例模式.单例模式是指创建唯一对象吗,单例模式设计的类只能实例化一个对象.

#!/usr/bin/python
# -*- coding: UTF-8 -*-

class Singleton(object):
    __instance = None                       # 定义实例

    def __init__(self):
        pass

    def __new__(cls, *args, **kwd):         # 在__init__之前调用
        if Singleton.__instance is None:    # 生成唯一实例
            Singleton.__instance = object.__new__(cls, *args, **kwd)
        return Singleton.__instance

   

__getattr__()、__setattr__()和__getattribute__():
当读取对象的某个属性时,python会自动调用__getattr__()方法.例如,fruit.color将转换为fruit.__getattr__(color).当使用赋值语句对属性进行设置时,python会自动调用__setattr__()方法.__getattribute__()的功能与__getattr__()类似,用于获取属性的值.但是__getattribute__()能提供更好的控制,代码更健壮.注意,python中并不存在__setattribute__()方法.
代码例子:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

class Fruit(object):
    def __init__(self, color = "red", price = 0):
        self.__color = color
        self.__price = price
        
    def __getattribute__(self, name):               # 获取属性的方法
        return object.__getattribute__(self, name)

    def __setattr__(self, name, value):
        self.__dict__[name] = value

if __name__ == "__main__":
    fruit = Fruit("blue", 10)
    print fruit.__dict__.get("_Fruit__color")       # 获取color属性
    fruit.__dict__["_Fruit__price"] = 5
    print fruit.__dict__.get("_Fruit__price")       # 获取price属性

   

__getitem__():
如果类把某个属性定义为序列,可以使用__getitem__()输出序列属性中的某个元素.假设水果店中销售多钟水果,可以通过__getitem__()方法获取水果店中的没种水果
代码例子:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

class FruitShop:
     def __getitem__(self, i):      # 获取水果店的水果
         return self.fruits[i]      

if __name__ == "__main__":
    shop = FruitShop()
    shop.fruits = ["apple", "banana"]
    print shop[1]
    for item in shop:               # 输出水果店的水果
        print item,

输出为:
banana
apple banana

   

__str__():
__str__()用于表示对象代表的含义,返回一个字符串.实现了__str__()方法后,可以直接使用print语句输出对象,也可以通过函数str()触发__str__()的执行.这样就把对象和字符串关联起来,便于某些程序的实现,可以用这个字符串来表示某个类
代码例子:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

class Fruit:      
    '''Fruit类'''               #为Fruit类定义了文档字符串
    def __str__(self):          # 定义对象的字符串表示
        return self.__doc__

if __name__ == "__main__":
    fruit = Fruit()
    print str(fruit)            # 调用内置函数str()出发__str__()方法,输出结果为:Fruit类
    print fruit                 #直接输出对象fruit,返回__str__()方法的值,输出结果为:Fruit类

   

__call__():
在类中实现__call__()方法,可以在对象创建时直接返回__call__()的内容.使用该方法可以模拟静态方法,实现了__call__的方法,则对象可以通过函数调用的方式 去执行__call__里面的内容
代码例子:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

class Fruit:
    class Growth:        # 内部类
        def __call__(self):
            print "grow ..."

    grow = Growth()      # 调用Growth(),此时将类Growth作为函数返回,即为外部类Fruit定义方法grow(),grow()将执行__call__()内的代码
if __name__ == '__main__':
    fruit = Fruit()
    fruit.grow()         # 输出结果:grow ...
    Fruit.grow()         # 输出结果:grow ...


魔法方法
含义

基本的魔法方法
__new__(cls[, ...]) 1. __new__ 是在一个对象实例化的时候所调用的第一个方法
2. 它的第一个参数是这个类,其他的参数是用来直接传递给 __init__ 方法
3. __new__ 决定是否要使用该 __init__ 方法,因为 __new__ 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 __new__ 没有返回实例对象,则 __init__ 不会被调用
4. __new__ 主要是用于继承一个不可变的类型比如一个 tuple 或者 string
__init__(self[, ...]) 构造器,当一个实例被创建的时候调用的初始化方法
__del__(self) 析构器,当一个实例被销毁的时候调用的方法
__call__(self[, args...]) 允许一个类的实例像函数一样被调用:x(a, b) 调用 x.__call__(a, b)
__len__(self) 定义当被 len() 调用时的行为
__repr__(self) 定义当被 repr() 调用时的行为
__str__(self) 定义当被 str() 调用时的行为
__bytes__(self) 定义当被 bytes() 调用时的行为
__hash__(self) 定义当被 hash() 调用时的行为
__bool__(self) 定义当被 bool() 调用时的行为,应该返回 True 或 False
__format__(self, format_spec) 定义当被 format() 调用时的行为
  有关属性
__getattr__(self, name) 定义当用户试图获取一个不存在的属性时的行为
__getattribute__(self, name) 定义当该类的属性被访问时的行为
__setattr__(self, name, value) 定义当一个属性被设置时的行为
__delattr__(self, name) 定义当一个属性被删除时的行为
__dir__(self) 定义当 dir() 被调用时的行为
__get__(self, instance, owner) 定义当描述符的值被取得时的行为
__set__(self, instance, value) 定义当描述符的值被改变时的行为
__delete__(self, instance) 定义当描述符的值被删除时的行为
  比较操作符
__lt__(self, other) 定义小于号的行为:x < y 调用 x.__lt__(y)
__le__(self, other) 定义小于等于号的行为:x <= y 调用 x.__le__(y)
__eq__(self, other) 定义等于号的行为:x == y 调用 x.__eq__(y)
__ne__(self, other) 定义不等号的行为:x != y 调用 x.__ne__(y)
__gt__(self, other) 定义大于号的行为:x > y 调用 x.__gt__(y)
__ge__(self, other) 定义大于等于号的行为:x >= y 调用 x.__ge__(y)
  算数运算符
__add__(self, other) 定义加法的行为:+
__sub__(self, other) 定义减法的行为:-
__mul__(self, other) 定义乘法的行为:*
__truediv__(self, other) 定义真除法的行为:/
__floordiv__(self, other) 定义整数除法的行为://
__mod__(self, other) 定义取模算法的行为:%
__divmod__(self, other) 定义当被 divmod() 调用时的行为
__pow__(self, other[, modulo]) 定义当被 power() 调用或 ** 运算时的行为
__lshift__(self, other) 定义按位左移位的行为:<<
__rshift__(self, other) 定义按位右移位的行为:>>
__and__(self, other) 定义按位与操作的行为:&
__xor__(self, other) 定义按位异或操作的行为:^
__or__(self, other) 定义按位或操作的行为:|
  反运算
__radd__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rsub__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rmul__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rtruediv__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rfloordiv__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rmod__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rdivmod__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rpow__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rlshift__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rrshift__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__rxor__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
__ror__(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
  增量赋值运算
__iadd__(self, other) 定义赋值加法的行为:+=
__isub__(self, other) 定义赋值减法的行为:-=
__imul__(self, other) 定义赋值乘法的行为:*=
__itruediv__(self, other) 定义赋值真除法的行为:/=
__ifloordiv__(self, other) 定义赋值整数除法的行为://=
__imod__(self, other) 定义赋值取模算法的行为:%=
__ipow__(self, other[, modulo]) 定义赋值幂运算的行为:**=
__ilshift__(self, other) 定义赋值按位左移位的行为:<<=
__irshift__(self, other) 定义赋值按位右移位的行为:>>=
__iand__(self, other) 定义赋值按位与操作的行为:&=
__ixor__(self, other) 定义赋值按位异或操作的行为:^=
__ior__(self, other) 定义赋值按位或操作的行为:|=
  一元操作符
__neg__(self) 定义正号的行为:+x
__pos__(self) 定义负号的行为:-x
__abs__(self) 定义当被 abs() 调用时的行为
__invert__(self) 定义按位求反的行为:~x
  类型转换
__complex__(self) 定义当被 complex() 调用时的行为(需要返回恰当的值)
__int__(self) 定义当被 int() 调用时的行为(需要返回恰当的值)
__float__(self) 定义当被 float() 调用时的行为(需要返回恰当的值)
__round__(self[, n]) 定义当被 round() 调用时的行为(需要返回恰当的值)
__index__(self) 1. 当对象是被应用在切片表达式中时,实现整形强制转换
2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 __index__
3. 如果 __index__ 被定义,则 __int__ 也需要被定义,且返回相同的值
  上下文管理(with 语句)
__enter__(self) 1. 定义当使用 with 语句时的初始化行为
2. __enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定
__exit__(self, exc_type, exc_value, traceback) 1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么
2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作
  容器类型
__len__(self) 定义当被 len() 调用时的行为(返回容器中元素的个数)
__getitem__(self, key) 定义获取容器中指定元素的行为,相当于 self[key]
__setitem__(self, key, value) 定义设置容器中指定元素的行为,相当于 self[key] = value
__delitem__(self, key) 定义删除容器中指定元素的行为,相当于 del self[key]
__iter__(self) 定义当迭代容器中的元素的行为
__reversed__(self) 定义当被 reversed() 调用时的行为
__contains__(self, item) 定义当使用成员测试运算符(in 或 not in)时的行为
 

1、自定义类的字符串的格式化,需要在类上面定义__format__ ():
_formats = {
'ymd' : '{d.year}-{d.month}-{d.day}',
'mdy' : '{d.month}/{d.day}/{d.year}',
'dmy' : '{d.day}/{d.month}/{d.year}'
}
class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def __format__(self, code):
        if code == '':
        code = 'ymd'
        fmt = _formats[code]
        return fmt.format(d=self)
测试:
>>> d = Date(2012, 12, 21)
>>> format(d)
'2012-12-21'
>>> format(d, 'mdy')
'12/21/2012'
>>> 'The date is {:ymd}'.format(d)
'The date is 2012-12-21'
>>> 'The date is {:mdy}'.format(d)
'The date is 12/21/2012'


2、让对象支持上下文管理协议(with),为了让一个对象兼容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 RuntimeError('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
这个类的关键特点在于它表示了一个网络连接,但是初始化的时候并不会做任何事情(比如它并没有建立一个连接)。连接的建立和关闭是使用with 语句自动完成的。例如:
from functools import partial
conn = LazyConnection(('www.python.org', 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''))
实现了socket的自动创建和关闭。
当出现with语句的时候,对象的__enter__ () 方法被触发,它返回的值(如果有的话) 会被赋值给as 声明的变量。然后,with 语句块里面的代码开始执行。最后, __exit__ () 方法被触发进行清理工作不管with 代码块中发生什么,上面的控制流都会执行完,就算代码块中发生了异常也是一样的。事实上, exit () 方法的三个参数包含了异常类型、异常值和追溯信息(如果有的话)。exit () 方法能自己决定怎样利用这个异常信息,或者忽略它并返回一个None 值。如果exit () 返回True ,那么异常会被清空,就好像什么都没发生一样, with 语句后面的程序继续在正常执行.在需要管理一些资源比如文件、网络连接和锁的编程环境中,使用上下文管理器是很普遍的

3、创建大量对象时,节省内存空间
对于主要是用来当成简单的数据结构类而言,可以通过给类添加__slots__属性来极大的减少实例所占用的内存比如:
class Date:
    __slots__ = ['year', 'month', 'day']
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
当你定义slots 后,Python 就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在slots 中列出的属性名在内部被映射到这个数组的指定小标上。使用slots 一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在slots中定义的那些属性名.关于slots 的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用slots 可以达到这样的目的,但是这个并不是它的初衷。slots更多的是用来作为一个内存优化工具。

4、在类中封装属性名
Python 程序员不去依赖语言特性去封装数据,而是通过遵循一定的属性和方法命名规约来达到这个效果。第一个约定是任何以单下划线开头的名字都应该是内部实现。比如:
class A:
    def __init__(self):
        self._internal = 0 # An internal attribute
        self.public = 1 # A public attribute
    def public_method(self):
        '''A public method'''
        pass
    def _internal_method(self):
        pass
Python 并不会真的阻止别人访问内部名称。但是如果你这么做肯定是不好的,可能会导致脆弱的代码。你还可能会遇到在类定义中使用两个下划线( __) 开头的命名
class B:
    def __init__(self):
        self.__private = 0
    def __private_method(self):
        pass
    def public_method(self):
        pass
使用双下划线开始会导致访问名称变成其他形式。比如,在前面的类B 中,私有属性会被分别重命名为_B_private 和_B_private_method 。这时候你可能会问这样重命名的目的是什么,答案就是继承——这种属性通过继承是无法被覆盖的

5、创建新的类或实例属性
如果你想创建一个新的拥有一些额外功能的实例属性类型,比如类型检查。那么久可以通过一个描述器类的形式来定义它的功能。下面是一个例子:
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(value, int):
            raise TypeError('Expected an int')
        instance.__dict__[self.name] = value
        
    def __delete__(self, instance):
        del instance.__dict__[self.name]
一个描述器就是一个实现了三个核心属性访问操作(get、set、delete)的类, 分别为__get__()、__set__()、__delete__()这三个特殊的方法, 这些方法接收一个实例作为输入,之后相应的操作实例底层的字典。为了使用一个描述器,需要将这个描述器的实例作为类属性放到一个类的定义中,例如:
class Point:
    x = Integer('x')
    y = Integer('y')
    def __init__(self, x, y):
        self.x = x
        self.y = y
当这样做之后,所有对描述器属性(x或者y)的访问都会被__get__()、__set__()、__delete__() 所捕获到。测试:
>>> p = Point(2, 3)
>>> p.x # Calls Point.x.__get__(p,Point)
2
>>> p.y = 5 # Calls Point.y.__set__(p, 5)
>>> p.x = 2.3 # Calls Point.x.__set__(p, 2.3)
Traceback (most recent call last):
File "", line 1, in 
File "descrip.py", line 12, in __set__
raise TypeError('Expected an int')
TypeError: Expected an int
>>>
描述器的一个比较困惑的地方是它只能在类级别被定义,而不能为每个实例单独定义。因此下面的代码将无法工作:
# Does NOT work
class Point:
    def __init__(self, x, y):
        self.x = Integer('x') # No! Must be a class variable
        self.y = Integer('y')
        self.x = x
        self.y = y
描述器通常是那些使用到装饰器或者元类的大型框架中的一个组件,同时它们的使用也 被隐藏在后面,下面是一个更高级的基于描述器的代码,并涉及到了一个类装饰器:
# Descriptor for a type-checked attribute
class Typed:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type
        
    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(value, self.expected_type):
            raise TypeError('Expected ' + str(self.expected_type))
        instance.__dict__[self.name] = value
        
    def __delete__(self, instance):
        del instance.__dict__[self.name]
        
# Class decorator that applies it to selected attributes
def typeassert(**kwargs):
    def decorate(cls):
        for name, expected_type in kwargs.items():
            # Attach a Typed descriptor to the class
            setattr(cls, name, Typed(name, expected_type))
            return cls
        return decorate
    
# Example use
@typeassert(name=str, shares=int, price=float)
class Stock:
    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price
Typed类描述器定义了 属性和预期属性类型的功能, typeassert 类装饰器实现了带 字典参数的类装饰器,通过传入的字典对所装饰的类遍历设置 描述器属性。 最后要指出的一点是,如果你只是想简单的自定义某个类的单个属性访问的话就不用去写描述器,这种情况下使用@property装饰器会更加容易。当程序中又很多重复代码的时候装饰器就有用了。

6、使用延迟计算属性
如果希望一个只读属性定义成一个Property,并且只有在访问的时候才会去计算结果,但是一旦属性被访问之后,你希望结果被缓存起来,不用每次去计算。实现如下:
import math
class lazyproperty:
    def __init__(self,func):
        self._func = func

    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            value = self._func(instance)
            setattr(instance,self._func.__name__,value)
            return value


class Circle()
    def __init__(self,radius):
        self.radius = radius

    @lazyproperty
    def area(self):
        print("Compute Area")
        return math.pi * self.radius**2
当一个描述器被放入一个类的定义时,每次访问属性时它的__get__()  __set__() 和__delete__()方法就会被触发,不过如果一个描述器仅仅只定义了 __get__()方法的话,它比通常就具有更弱的绑定,特别的,只有当被访问的属性不存在实例字典中时 __get__()方法才会被触发。如上例,当第一次访问 Circle对象的area属性的时候,实例字典中没有该属性,则进入描述器的__get__()方法,然后__get()方法通过 装饰器修饰的area函数名 和调用所得的值设置到实例中,此后该实例就有了 area方法。

7、简化数据结果的初始化
可以在一个基类中写一个公用的__init__()函数:
class Base():
    _fields = []
    def __init__(self,*args):
        if len(args) != len(self._fields):
            raise TypeError("Expected {} arguments".format(len(self._fields)))
        for name, value in zip(self._fields,args):
            setattr(self,name,value)

class Stock(Base):
    _fields = ['name','shares','price']

class Point(Base):
    _fields = ['x','y']
使用实例:
>>> s = Stock('ACME', 50, 91.1)
>>> p = Point(2, 3)
>>> s2 = Stock('ACME', 50)
Traceback (most recent call last):
File "", line 1, in
File "structure.py", line 6, in __init__
raise TypeError('Expected {} arguments'.format(len(self._fields)))
TypeError: Expected 3 arguments

如果还想支持关键字参数,可以将关键字参数设置为实例属性:
class Base2():
    _fields = []
    def __init__(self,*args,**kwargs):
        if len(args) > len(self._fields):
            raise TypeError('Expected {} arguments'.format(len(self._fields)))
        for name,value in zip(self._fields,args)
            setattr(self,name,value)
        for name in self._fields[len(args):]:
            setattr(self,name,kwargs.pop(name))
        if kwargs:
            raise TypeError('invalid argument(s):{}'.format(','.join(kwargs)))
其他定义额外的 关键字参数 只要修改__init__方法中的实现就可以了,总体思路 设置到 实例的 属性中去

8、定义接口或者抽象基类
定义一个接口或者抽象类,并且通过执行类型检查来确保子类实现了某些特定的方法,使用abc模块可以很轻松的定义抽象基类。
from abc import ABCMeta,abstractmethod
class ISteram(metaclass=ABCMeta):
    @abstractmethod
    def read(self,maxbytes = -1):
        pass

    @abstractmethod
    def write(self,data):
        pass
抽象类的一个特点是它不能被实例化。抽象类的目的是让别的类继承它并实现特定的抽象方法:
class SocketStream(ISteram):
    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
除了继承这种方法之外,还可以通过注册方式来让某个类实现抽象基类:
import io
# Register the built-in I/O classes as supporting our interface
IStream.register(io.IOBase)
# Open a normal file and type check
f = open('foo.txt')
isinstance(f, IStream) # Returns True
@abstractmethod还能注解静态方法、类方法和 properties。你只需要保证这个注解紧靠在函数定义之前即可。
class D(metaclass=ABCMeta):
    @property
    @abstractmethod
    def name(self):
        pass

    @name.setter
    @abstractmethod
    def name(self,value):
        pass

    @classmethod
    @abstractmethod
    def methon1(cls):
        pass

    @staticmethod
    @abstractmethod
    def method2():
        pass

9、实现数据模型的类型约束
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

class Typed(Descriptor):
    excepted_type = type(None)
    def __set__(self, instance, value):
        if not isinstance(value,self.excepted_type):
            raise TypeError('excepted ' + str(self.excepted_type))
        super().__set__(instance,value)

class Unsigned(Descriptor):
    def __set__(self, instance, value):
        if value < 0:
            raise ValueError('except >= 0')
        super().__set__(instance,value)

class MaxSized(Descriptor):
    def __init__(self,name=None,**opts):
        if 'size' not in opts:
            raise TypeError('miss 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 Interger(Typed):
    excepted_type = int

class UnsignedInterger(Interger,Unsigned):
    pass

class Float(Typed):
    excepted_type = float

class UnsignedFloat(Float,Unsigned):
    pass

class String(Typed):
    excepted_type = str

class SizedString(String,MaxSized):
    pass

#使用这些自定义数据类型,我们自定义一个类
class Stock():
    name = SizedString('name',size=8)
    shares = UnsignedInterger('shares')
    price = UnsignedFloat('price')

    def __init__(self,name_,shares,price):
        self.name = name_
        self.shares = shares
        self.price = price

s1 = Stock('acm',100,50.0)

10、实现自定义的容器
想要实现自定义的类来模拟内置的容器类功能,比如列表和字典。collections定义了很多抽象基类,当你想要自定义容器类的时候,会很有用。比如想要自己实现的类支持迭代,那就从collections.Iterable类继承。例如:
import collections
class A(collections.Iterable):
    def __init__(self,fruits):
        self.fruits = fruits

    def __iter__(self):
        for item in self.fruits:
            yield item

a = A(["a","b","c","d"])
for key in a:
    print(key)
从collections.Iterable继承的类需要实现基类的抽象方法 __iter__()  否则会报错。
比如想要自己实现的类支持序列操作、切片操作,可以从collections.Sequence类继承,并且实现基类的抽象函数__len__  和 __getitem__ 。比如:
import collections
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))
print(items[0:2])
上面用到了bisect模块,它是一个在排序列表中插入元素的高效方式,可以保证元素插入后还保持顺序。 输出结果:
[1, 3, 5]
(1, 5)
[1, 2, 3, 5]
[1, 2]
使用collections模块中的抽象基类可以确保你自定义的容器实现了所有必要的方法,并且还能简化类型检查。提供一些常见的容器操作提供默认的实现,这样一来你只需要实现那些你感兴趣的方法即可。

11、属性的代理访问
如果你想要将某个实例的属性访问代理到内部的另外一个实例中去,目的可能是作为继承的一个替代方法或者实现代理模式。简单来说,代理是一种编程模式,它将摸个操作转移到另外一个对象来实现。最简单的实现如下:
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
但是如果有大量的方法需要代理,那么使用 __getattr__()方法或许更好些。比如:
class B2():
    def __init__(self):
        self._a = A()

    def bar(self):
        pass

    def __getattr__(self, item):
        "这个方法在访问的attribute不存在的时候调用"
        return getattr(self._a,item)
当去访问对象的属性的时候,最开始B2没有 方法属性 spam  和 foo ,当代码中去调用改方法的时候 会执行__getattr__方法,然后从A对象的实例中返回对应的属性,即返回a实例中的方法,实现代理。
另外一个代理例子是实现代理模式,例如:
class Proxy:
    def __init__(self,obj):
        self._obj = obj

    def __getattr__(self, item):
        return getattr(self._obj,item)

    def __setattr__(self, key, value):
        if key.startswith('_'):
            super().__setattr__(key,value)
        else:
            setattr(self._obj,key,value)

    def __delattr__(self, item):
        if item.startswith('_'):
            super().__delattr__(item)
        else:
            delattr(self._obj,item)

class Spam:
    def __init__(self,x):
        self.x = x
    def bar(self,y):
        print('Spam.bar:',self.x,y)
__getattr__()实际是一个后备方法,只有在属性不存在的时候才会调用。因此,如果代理类实例本身有这个属性的话,那么久不会触发这个方法的。另外__setattr__() 和 __delattr__() 需要额外的魔法来区分代理实例和被代理实例的_obj属性。一个通常的预定是只代理那些不以下划线开头的属性。

12、在类中定义多个构造器
如果你想实现一个类,除了使用__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)

13、创建一个不调用__init__()方法的实例
可以通过__new__()方法创建一个未初始化的实例,直接使用 类.__new__()

14、利用Mixins拓展类功能
假如你又很多有用的方法,想使用它们来拓展其他类的功能,但是这些类并没有任何继承的关系,因此你不能简单的将这些方法放到一个基类中,然后被其他类继承。假设你想拓展映射对象,给它们添加日志、唯一性设置、类型检查等等功能。下面是一些混入类:
class LoggedMappingMixin:
    "添加logging到get set delete 操作,在调试模式下"
    __slots__ = () #混入类没有实例变量,因为直接实例化混入类没有任何意义
    def __getitem__(self, item):
        print("Getting "+ str(item))
        return super().__getitem__(item)

    def __setitem__(self, key, value):
        print("Setting {} = {!r}".format(key,value))
        return super().__setitem__(key,value)

    def __delitem__(self, key):
        print("Delete " + str(key))
        return super().__delitem__(key)

class SetOnceMappingMixin:
    "仅仅允许值被设置一次"
    __slots__ = ()
    def __setitem__(self, key, value):
        if key in self:
            raise KeyError(str(key)+ ' already set')
        else:
            return super().__setitem__(key,value)


class StringKeysMappingMixin:
    "键值只允许是字符串"
    __slots__ = ()
    def __setitem__(self, key, value):
        if not isinstance(key,str):
            raise TypeError('key must be strings')
        else:
            super().__setitem__(key,value)
这些类单独使用起来没有任何意义,事实上如果你去实例化任何一个类,除了产生异常外没任何作用。它们是用来通过多继承来和其他映射对象混入使用的。例如:
class LoggedDict(LoggedMappingMixin,dict):
    pass

d = LoggedDict()
d['x'] = 23
print(d['x'])
del d['x']
输出结果:
Setting x = 23
Getting x
23
Delete x
还有一种使用混入类的方式是使用类装饰器。例如:
def LoggedMapping(cls):
    """ 第二种方式:使用类装饰器"""
    cls_getitem = cls.__getitem__
    cls_setitem = cls.__setitem__
    cls_delitem = cls.__delitem__
    def __getitem__(self, key):
        print('Getting ' + str(key))
        return cls_getitem(self, key)

    def __setitem__(self, key, value):
        print('Setting {} = {!r}'.format(key, value))
        return cls_setitem(self, key, value)
    
    def __delitem__(self, key):
        print('Deleting ' + str(key))
        return cls_delitem(self, key)
    cls.__getitem__ = __getitem__
    cls.__setitem__ = __setitem__
    cls.__delitem__ = __delitem__
    return cls

@LoggedMapping
class LoggedDict(dict):
    pass

15、实现状态对象或者状态机
如果你想事项一个状态机或者在不同状态下执行操作对象,但是又不想在代码中出现太多的条件判断语句。解决方法为每个状态定义一个对象:
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 CloseConnectionState(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('alread closed')

class OpenConnectionState:
    @staticmethod
    def read(conn):
        print('reading')

    @staticmethod
    def write(conn,data):
        print('write')

    @staticmethod
    def open(conn):
        raise RuntimeError('already open')

    @staticmethod
    def close(conn):
        conn.new_state(CloseConnectionState)
    
16、让类支持比较操作
如果让某个类支持标准的比较运算(比如>= ,!=, <= , <)等,但是又不想去实现那一大堆的特殊方法。使用装饰器functools.total_ordering来简化这个处理,使用它装饰一个类,只需要定义一个__eq__()方法,外加(__lt__、  __gt__、  __le__、 __ge__ )中的一个就可以了。然后装饰器会自动填充其他方法。

你可能感兴趣的:(Python)