与jvm上的语言不一样,python的语言没有interface关键字,而且除了抽象基类,每个类都有相应的接口:类实现或继承的公开属性(方法或数据类型)
在定义里,受保护的属性和私有属性不在接口中:即便“受保护的”属性也只是采用名称改写,私有属性也是可以轻松访问。
公开数据属性是可以变成特性,可以转换成只读,例如:
class test():
def __init__(self,x):
self.__x = x
@property
def x(self):
return self.__x
a = test(3)
a.x
Out[3]: 3
接口是实现特定方法的集合,协议和继承没有关系,一个类可能实现多个接口,从而让实例扮演多个角色。
接下来再继续看看序列协议。我们定义一个类:
class test1():
def __getitem__(self,pos):
return range(3)[pos]
a = test1()
a[1]
Out[13]: 1
2 in a
Out[14]: True
for i in a:
print(i)
0
1
2
在这个类,我们只是实现了__getitem__,python自动就帮你实现了迭代,contains方法。
协议的动态本性:
python里面有个方法shuffle,可以就地打乱序列,如下:
from random import shuffle
l = list(range(3))
shuffle(l)
l
Out[23]: [1,2,0]
但是我们应用在上面那个例子:
class test1():
def __init__(self):
self.alist = list(range(20))
def __getitem__(self,pos):
print(pos)
return self.alist[pos]
def __len__(self):
return len(range(20))
a = test1()
shuffle(a)
Traceback (most recent call last):
File "<ipython-input-49-ef6bc136c33f>",line 1,in <module>
shuffle(a)
File "D:\Users\chuancy\AppData\Local\Continuum\Anaconda3\lib\random.py",line 272,in shuffle
x[i],x[j] = x[j],x[i]
TypeError: 'test1' object does not support item assignment
我们可以发现shuffle函数需要调换集合中元素的位置,而test1实现的是不可变序列,可变序列还需要实现__setitem__方法
def setitem(self,key,value):
self.alist[key] = value
test1.__setitem__ = setitem
shuffle(a)
9
19
10
setitem的参数的名只是约定一样,只不过python会往里面传参数,类的方法本质上也只是一个函数而已。
我们可以在test1已经实例化后,再设置__setitem__,在运行时修改类和模块,而不改动源码,这就是所谓的“猴子补丁”。
“鸭子类型”:忽略对象的真正类型,转而关注对象有没有实现所需的方法,签名和语义。
继承抽象基类很简单,只要实现python里的特殊方法__len__之类的,这样python就会自动识别。
抽象基类的继承大多都是在collections模块,现在打开这个模块的文档看看。
ABC Inherits from Abstract Methods Mixin Methods
Container __contains__(支持in)
Hashable __hash__
Iterable __iter__(支持可迭代)
Iterator Iterable next __iter__
Sized __len__(支持len)
Callable __call__
Sequence Sized,Iterable,Container __getitem__,__len__ __contains__,__iter__,__reversed__,index,and count
MutableSequence Sequence __getitem__,__setitem__,__delitem__,__len__,insert Inherited Sequence methods and append,reverse,extend,pop,remove,and __iadd__
Set Sized,Iterable,Container __contains__,__iter__,__len__ __le__,__lt__,__eq__,__ne__,__gt__,__ge__,__and__,__or__,__sub__,__xor__,and isdisjoint
MutableSet Set __contains__,__iter__,__len__,add,discard Inherited Set methods and clear,pop,remove,__ior__,__iand__,__ixor__,and __isub__
Mapping Sized,Iterable,Container __getitem__,__iter__,__len__ __contains__,keys,items,values,get,__eq__,and __ne__
MutableMapping Mapping __getitem__,__setitem__,__delitem__,__iter__,__len__ Inherited Mapping methods and pop,popitem,clear,update,and setdefault
MappingView Sized __len__
ItemsView MappingView,Set __contains__,__iter__
KeysView MappingView,Set __contains__,__iter__
ValuesView MappingView __contains__,__iter__
从官方文档中可以看出,一共有16个基类,分为三层:
--顶层是Iterable,Container,Sized,Callable,Hashable:
其中Iterable,Container,Sized是各个集合该继承的三个抽象基类,或者至少实现兼容的协议。
Callable,Hashable只要是为isinstance提供支持,并且计算hash值
--再往下是Sequence,Mapping,Set,MappingView,Iterator
Sequence,Mapping,Set是不可变类型,都有自己可变的子类
MappingView是散列表的映射,.items(),.keys(),.values()返回的对象的是ItemView,KeysView,ValuesView
在_collections_abc.py中
class Hashable(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __hash__(self):
return 0
@classmethod
def __subclasshook__(cls, C):
if cls is Hashable:
for B in C.__mro__:
if "__hash__" in B.__dict__:
if B.__dict__["__hash__"]:
return True
break
return NotImplemented
取出一个来看__subclasshook__这个方法,这个是用于检查继承的类是否确实是Hashable的子类,也就是判断是否存在__hash__