python的抽象基类

与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__

原文发布于微信公众号 - 鸿的学习笔记(shujuxuexizhilu)

原文发表时间:2017-08-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏博岩Java大讲堂

Java虚拟机--你的对象有多大如何计算对象大小

4525
来自专栏阿杜的世界

Java Web技术经验总结(十五)

1313
来自专栏章鱼的慢慢技术路

Go指南_指针接收者

1032
来自专栏佳爷的后花媛

java基础知识

Vector、Stack、HashTable、ConcurrentHashMap、Properties

3015
来自专栏Titan框架

Titan Framework MongoDB深入理解2

在上一篇文章中,我们提到了Titan框架集成的MongoDB功能内部一些比较重要的顶级接口,但我们平时接触到比较多的还是Curd和Finder这两个类,他们是T...

904
来自专栏流柯技术学院

TestNG 三 测试方法

测试方法是可以带有参数的。每个测试方法都可以带有任意数量的参数,并且可以通过使用TestNG的@Parameters向方法传递正确的参数。

1143
来自专栏自学笔记

python基本常识

tuple,str都可以看做是一种list,都可以进行切片操作。 利用切片操作,去掉一个字符串的前后空格。要注意是是前后空格是不止一个的,可能有很多个。

3465
来自专栏伪君子的梦呓

题解 ~ 简单的a+b ~ C++ 做法

1144
来自专栏yukong的小专栏

【java并发编程实战3】解密volatilevolatile的使用场景

根据 as if serial原则,它强调了单线程。那么多线程发生重排序又是怎么样的呢?

782
来自专栏恰童鞋骚年

《C#图解教程》读书笔记之二:存储、类型和变量

  (1)C程序是一组函数和数据类型,C++程序是一组函数和类,而C#程序是一组类型声明;

773

扫码关注云+社区

领取腾讯云代金券