1. 什么是鸭子类型

当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子, 那么这只鸟就可以被称为鸭子.

在下面这段代码中, 所有的类实现了同一个方法, 这些类就可以归为同一种类型, 这样在调用的时候就可以都调用say方法, 从而实现了多态, 一种接口多种实现.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Cat(object):
def say(self):
print("i am a cat")

class Dog(object):
def say(self):
print("i am a fish")

class Duck(object):
def say(self):
print("i am a duck")

animal_list = [Cat, Dog, Duck] # 类也是对象, 可以被添加到列表中, 而且python是动态语言, 对于变量不需要指定类型
for animal in animal_list:
animal().say()

1.1 列表的extend

1
2
def extend(self, iterable): # real signature unknown; restored from __doc__
""" L.extend(iterable) -> None -- extend list by appending elements from the iterable """

list.extend()
因为传入的只需要是iterable类型即可, 所以list可以扩展tuple、set等都可以. 或者我们实现一个类可以迭代,也可以放到extend中.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

def __getitem__(self, item): # 可迭代
return self.employee[item]

company = Company(["tom", "bob", "jane"])

a = ["bobby1", "bobby2"]

a.extend(company)
print(a)

我们在几个对象中都实现了某一个方法名, 这些类我们就可以通用, 比如上面的say和__getitem__, 而魔法函数也是利用的python的鸭子类型, 只要实现了__getitem__就可迭代, 就可以传入extend中.

2. 抽象基类(abc模块)

2.1 抽象基类

  • 抽象基类是一个虚拟的类, 相当于一个模板, 定义一些方法, 所有继承这个基类的类必须覆盖抽象基类里面的方法
  • 抽象基类是无法用来实例化的

2.2 为什么要有抽象基类

因为python是基于鸭子类型的, 所以其实只要实现某些方法就可以了, 那为什么还要抽象基类呢?

  • 第一种用法:我们去检查某个类是否有某一种方法

某些情况之下希望判定某个对象的类型, 可以使用hasattr判断是否实现某方法或者使用isinstance(推荐)去判断一个类是否是指定的类型, Sized就是一个实现__len__的抽象基类.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list

def __len__(self):
return len(self.employee)

com = Company(["ren1","ren2"])
print(hasattr(com, "__len__"))

from collections.abc import Sized
print(isinstance(com, Sized))
>>> True

# Sized内部的类方法, 在调用isinstance(com, Sized)时cls就是Sized对象, C就是com对象,然后判断com对象有没有实现__len__方法.
@classmethod
def __subclasshook__(cls, C):
if cls is Sized:
return _check_methods(C, "__len__")
return NotImplemented

# isinstance还会找到继承链去进行判断
class A:
pass

class B(A):
pass

b = B()
print(isinstance(b, A))
>>> True
  • 第二种用法: 强制某个子类必须实现某些方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 模拟抽象基类, 只有在调用set方法的时候才会抛出异常
class CacheBase():
def get(self, key):
raise NotImplementedError
def set(self, key, value):
raise NotImplementedError

class RedisCache(CacheBase):
def set(self, key, value):
pass

redis_cache = RedisCache()
redis_cache.set("key", "value")

# 使用abc模块, 在初始化的时候就会去判断有没有重载基类的方法,没有就抛异常
import abc

class CacheBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def get(self, key):
pass

@abc.abstractmethod
def set(self, key, value):
pass

class RedisCache(CacheBase):
def set(self, key, value):
pass
def get(self, key):
pass

redis_cache = RedisCache()

2.3 collection.abc模块

在这个模块中定义了很多通用的抽象基类, 比如Sized. 但是这些抽象基类定义出来并不是用来继承的, 更多的是让我们理解接口的一些定义. 推荐使用鸭子类型或者多继承(Mixin)实现, 而少用抽象基类.

1
2
3
4
5
6
7
8
9
10
__all__ = ["Awaitable", "Coroutine",
"AsyncIterable", "AsyncIterator", "AsyncGenerator",
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
"Sized", "Container", "Callable", "Collection",
"Set", "MutableSet",
"Mapping", "MutableMapping",
"MappingView", "KeysView", "ItemsView", "ValuesView",
"Sequence", "MutableSequence",
"ByteString",
]

2.4 声明抽象基类

  • metaclass = abc.ABCMeta
  • @abc.abstractmethod

3. type与isinstance区别

  1. type用于求一个未知数据类型的对象,isinstance用于判断一个对象是否是已知类型;
  2. type不认为子类是父类的一种类型,isinstance认为子类是父类的一种类型,即子类对象也属于父类类型.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class A:
pass

class B(A):
pass

b = B()

# instance用于判断一个对象是否是已知类型
print(isinstance(b, B)) # True
print(isinstance(b, A)) # True isinstance会根据继承链去判断

# 求一个未知数据类型的对象
print(type(b)) # <class '__main__.B'>

# type不认为子类是父类的一种类型,isinstance认为子类是父类的一种类型,即子类对象也属于父类类型.

# is是去判断这两个是不是一个对象, 即id是否相同
# ==是判断值是否相等
print(type(b) is B) # True 得到的是B的地址
print(type(b) is A) # False

print(id(B)) # 1943999460392
print(id(type(b))) # 1943999460392
print(B) # <class '__main__.B'>

a=[1,2,3]
b=[1,2,3]
c=a

print(id(a)) # 2065494827720
print(id(b)) # 2065493557192
print(id(c)) # 2065494827720

print(a is b) # False
print(c is a) # True
print((a==b)) # True