1、深入类和对象:鸭子类型和多态
动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
a = [1, 2]
b = [3, 4]
c = (5, 6) # tuple类型 元祖
d = {7, 8} # 集合类型 set 它的特性是无序的
a.extend(b)
print(a)
a.extend(c)
print(a)
a.extend(d)
print(a)
'''
def extend(self, iterable)
# 这是函数的源码部分,iterable代表可以迭代的 即只要方法存在,参数正确,就可以调用
'''
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。
class Cat(object):
def say(self):
print("i am cat")
class Dog(object):
def say(self):
print("i am Dog")
class Duck(object):
def say(self):
print("i am Duck")
'''
多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
序列类型有多种形态:字符串,列表,元组
动物有多种形态:人,狗,猪
'''
animal_list = [Cat, Dog, Duck] # 在调用Cat、Dog这些的时候,不知道它们是什么类型 ,当实例化之后就知道是一个类
for animal in animal_list:
animal().say() # 此时的animal后面需要加() 为了实例化
2、抽象基类(abc模块)
抽象基类(abstract base class,ABC):抽象基类就是类里定义了纯虚成员函数的类。纯虚函数只提供了接口,并没有具体实现。抽象基类不能被实例化(不能创建对象),通常是作为基类供子类继承,子类中重写虚函数,实现具体的接口。
抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。
抽象基类应用场景如下:
'''
应用场景
1、判断某个对象的类型
'''
class Demo(object):
def __init__(self, name):
self.name = name
def __len__(self): # 如果这里的__len__的方法注释掉 在下面的len(d)输出就会报错,因为需要重写len方法
return len(self.name)
def test(self): # 这里注释掉会造成后面的hasattr函数输出为False
pass
d = Demo(["1", "2", "chen"])
print(len(d))
print(hasattr(d, 'test')) # 判断d是否有test的方法
print(isinstance(1, int)) # 判断前面的值 1 是否是后面的 int 类型
print(isinstance('Demo', int)) # False
from collections.abc import Sized,Iterable
print(isinstance(d, Sized)) # True
# 因为这里会检查实例对象中有没有__len__方法,有即输出True
# 是python的源码中的c语言定义的 G:\python3.6.5\Lib\_collections_abc.py 源码文件路径
print(isinstance(d, Iterable)) # True 同上
'''
应用场景
2、我们需要强制某个子类必须实现某些方法
如果没有重写父类的方法,运行就报错
'''
class CacheBase(object):
def get(self, key):
raise ValueError # 第一种方法:主动抛出异常
def set(self, key, value):
raise NotADirectoryError
class RedisBase(CacheBase):
# 重写父类方法
def get(self, key):
pass
r = RedisBase()
r.get(1)
'''
应用场景
2、我们需要强制某个子类必须实现某些方法
如果没有重写父类的方法,运行就报错
'''
import abc # 第二种方法 引入abc模块
class CacheBase(metaclass=abc.ABCMeta): # 注意这里的语句与第一种方法的区别
@abc.abstractmethod
def get(self, key):
# raise ValueError # 第一种方法:主动抛出异常
pass
@abc.abstractmethod
def set(self, key, value):
# raise NotADirectoryError
pass
class RedisBase(CacheBase):
# 重写父类方法
def get(self, key):
pass
r = RedisBase()
r.get(1)
# 此时报错set方法 TypeError: Can't instantiate abstract class RedisBase with abstract methods set
# 因为此时的子类没有重写set方法,重写了get方法
3、isinstance和type的区别
– isinstance会考虑类的继承关系
– type不会考虑类的继承关系
i = 1
o = 'chen'
print(isinstance(i, int)) # True
print(isinstance(o, str)) # True
class A:
pass
class B(A):
pass
b = B()
print(isinstance(b, B)) # True
print(isinstance(b, A)) # True
# isinstance会考虑类的继承关系
print(type(b) is B) # True
# == 比较的是values值
# is 比较的是内存地址值
print(type(b) is A) # False
# type不会考虑类的继承关系
if isinstance(i , int): # 正常执行输出语句
print(123)
if type(i) == 'int': # 进入不了输出语句 不可以这样写
print(123)
4、类变量和对象变量
class A:
# 类属型
aa = 1
# 构造方法 实例方法
def __init__(self, x, y):
# 实例属性
self.x = x
self.y = y
# self.aa = 22
a = A(1, 2)
print(a.x, a.y, a.aa) # 1 2 1
# a.aa可以向上查找
# print(A.x) # 报错:AttributeError: type object 'A' has no attribute 'x'
# 不能向下查找
print(A(1, 2).x) # 1
A.aa = 11 # 相当于重新赋值
print(a.aa) # 11
a.aa = 22 # 相当于在实例方法中添加了一个语句: self.aa = 22
print(a.aa) # 22
print(A.aa) # 11
b = A(1, 2)
print(b.aa) # 11 相当于A.aa = 11
5、类属性和实例属性以及查找顺序
在Python2.3之后,Python采用了C3算法
**mro算法:**排序的顺序可以用mro函数显示
# 菱形继承关系图
class D:
pass
class B(D):
pass
class C(D):
pass
class A(B, C):
pass
print(A.__mro__) # 继承顺序:A B C D E
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
# 二叉树继承关系图
class D:
pass
class B(D):
pass
class E:
pass
class C(E):
pass
class A(B, C):
pass
print(A.__mro__) # A B D C E
# (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
6、Python对象的自省机制
自省是通过一定的机制查询到对象的内部结构
Python中比较常见的自省(introspection)机制(函数用法)有: dir(),type(), hasattr(), isinstance(),
通过这些函数,我们能够在程序运行时得知对象的类型,判断对象是否存在某个属性,访问对象的属性。
class Person(object):
name = 1
class Studet(Person):
def __init__(self, school_name):
self.school_name = school_name
user = Studet('2')
print(user.name) # 1
print(user.__dict__) # {'school_name': '2'} 显示属性
print(dir(user)) # 显示出所有user的属性 功能比dict强大
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'school_name']
7、super函数
在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现
作用:
1、重写了构造函数 为什么还要调用super?
# 1、重写了构造函数 为什么还要调用super?
class Person(object):
def __init__(self, name, age, weight):
self.name = name
self.age = age
self.weight = weight
def speak(self):
print("%s 说:我 %d 岁了!" % (self.name, self.age))
class Studet(Person):
def __init__(self, name, age, weight, grade):
# self.name = name
# self.age = age
# self.weight = weight
super().__init__(name, age, weight) # 这里super函数代替了上面的三行代码
Person.__init__(self, name, age, weight) # 这样也可以调用父类方法 注意要写self 可以代替上面的super函数
self.grade = grade
s = Studet(1, 18, 30, 2)
s.speak()
2、super 执行的顺序是什么样的
super函数 调用父类的方法是按照mro算法的顺序来调用的,根据C3算法顺序调用的 而不是按照父类顺序调用
# 2、super 执行的顺序是什么样的
# 菱形继承关系
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
print("B")
super().__init__()
class C(A):
def __init__(self):
print("C")
super().__init__()
class D(B, C):
def __init__(self):
print("D")
super().__init__()
d = D()
print(D.__mro__) # D B C A
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
# super函数 调用父类的方法是按照mro算法的顺序来调用的,根据C3算法顺序调用的 而不是按照父类顺序调用