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] for animal in animal_list: animal().say()
1.1 列表的extend
1 2 def extend (self, iterable ): """ 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 Sizedprint (isinstance (com, Sized))>>> True @classmethod def __subclasshook__ (cls, C ): if cls is Sized: return _check_methods(C, "__len__" ) return NotImplemented 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 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" ) import abcclass 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区别
type用于求一个未知数据类型的对象,isinstance用于判断一个对象是否是已知类型;
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() print (isinstance (b, B)) print (isinstance (b, A)) print (type (b)) print (type (b) is B) print (type (b) is A) print (id (B)) print (id (type (b))) print (B) a=[1 ,2 ,3 ] b=[1 ,2 ,3 ] c=a print (id (a)) print (id (b)) print (id (c)) print (a is b) print (c is a) print ((a==b))