深入类和对象
1. 鸭子对象和多态
1.1 鸭子类型
在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
- 示例代码
从上面的代码可以知道,dog类、cat类都不是鸭子,但是他们和duck类很像,都有say这个方法。class duck: def say(self): print("I am a duck") class dog: def say(self): print("I am a dog") class cat: def say(self): print("I am a cat") duck().say() dog().say() cat().say()
1.2 多态
- 什么是多态?
在需要使用父类对象的地方,也可以使用子类对象, 这种情况就叫多态.
比如, 在函数中,我需要调用 某一个父类对象的方法, 那么我们也可以在这个地方调用子类对象的方法. - 如何在程序中使用多态?
可以按照以下步骤实现代码: 1、子类继承父类 2、子类重写父类中的方法 3、通过对象调用这个方法
- 示例代码
class Father: def repair(self): print("父亲在修理...") class Son(Father): def repair(self): print("儿子在修理...") father = Father() father.repair() son = Son() son.repair()
2. 抽象基类
2.1 抽象基类介绍
抽象类(abstract base class,ABC)就是类里定义了纯虚成员函数的类。纯虚函数一般只提供了接口,并不会做具体实现(虽然可以),实现由它的派生类去重写。抽象类不能被实例化(不能创建对象),通常是作为基类供子类继承,子类中重写虚函数,实现具体的接口。简言之,ABC描述的是至少使用一个纯虚函数的接口,从ABC派生出的类将根据派生类的具体特征,使用常规虚函数来实现这种接口。
2.2 抽象基类应用场景
• 1.我们去检查某个类中是否有某种方法
• 2.我们需要强调某个子类必须实现某些方法
1.检查某个类中是否有某种方法
- 示例代码
class Demo: def __init__(self, x): self.x = x def __len__(self): return self.x demo = Demo("abcd") # 导入基类的Size from collections.abc import Sized # 判断是否含有某种方法 print(isinstance(demo, Sized)) # True,demo是Size的子类
2.我们需要强调某个子类必须实现某些方法
- 示例代码
import abc class A(metaclass=abc.ABCMeta): # 定义一个抽象方法 @abc.abstractclassmethod def dem(self): pass class B(A): # 这里必须继承父类的dem的抽象方法,否则在下面实例化的时候会报错 def dem(self): pass b = B()
注意: 如果python版本在3.3以上,则可以使用@classmethod
代替@abc.abstractclassmethod
3. type与isinstance
3.1 type与isinstance的区别
- 返回值区别
- type的返回值是对象的类型
- isinstance的返回值是Boolean类型
- 示例代码
str1 = 'abcd' print(type(str1)) # <class 'str'> print(isinstance(str1, str)) # True
- type与isinstance的区别
- type() 不会认为子类是一种父类类型,不考虑继承关系。
- isinstance() 会认为子类子类是一种父类类型,考虑继承关系
- 示例代码
class Father: pass class Son(Father): pass son = Son() print(isinstance(son, Son)) # True print(isinstance(son, Father)) # True 考虑继承 print(type(son) is Son) # True print(type(son) is Father) # False 不考虑继承关系
4. 类属性与实例属性
4.1 基本查找顺序
- 对象是可以向上查找的,所以可以访问到类属性
class Demo: temp = '你好' def __init__(self): pass demo = Demo() print(demo.temp) # 输出 你好,说明实例对象可以向上访问类属性
- 当对象自己有该实例属性时 ,则输出的是自己的
class Demo: temp = '你好' def __init__(self): self.temp = 'hello' demo = Demo() print(demo.temp) # 输出 hello,说明实例对象有与类属性相同的实例属性时,输出当前的属性值
- 类不能向下查找,所以只能访问到类属性
class Demo: temp = '你好' def __init__(self): self.temp = 'hello' self.t1 = 'good' demo = Demo() print(Demo.t1) # 报错type object 'Demo' has no attribute 't1',说明类不能向下查找,所以只能访问到类属性
4.2 多继承查询顺序
继承关系如下,则属性查找顺序为?
实际上,python2.2(金典类)之前的算法:MRO算法,DFS(deep first search) 深度优先。
如下图,菱形继承,执行顺序如何?
在python2.2版本之后,引入BFS(广度优先)。
在python新式类,就引入了C3算法,通过className.__mro__
来查看。
4. Python对象自省机制
自省是通过一定的机制查询到对象的内部结构
Python中比较常见的自省(introspection)机制(函数用法)有: dir(),type(), hasattr(), isinstance(),通过这些函数,我们能够在程序运行时得知对象的类型,判断对象是否存在某个属性,访问对象的属性。
- 代码示例
class Perpon(object): name = "码农" class Student(Perpon): def __init__(self, school_name): self.school_name = school_name hty = Student("宇宙学院") print(hty.__dict__) # {'school_name': '宇宙学院'} 当前对象的属性 {"属性":"属性的值"} print(dir(hty)) # [] 考虑到继承的成员
4.1 super函数
super() 函数是用于调用父类(超类)的一个方法。
- 代码示例
class Person(object): def __init__(self, name, age, weight): self.name = name self.age = age self.weight = weight def speak(self): print(f"{self.name}说:我{self.age}岁") class Student(Person): def __init__(self, name, age, weight, grade): # 因为父类已有name,age,weight三个属性,所以不必重新定义 super().__init__(name, age, weight) self.grade = grade def speak(self): print(f"{self.name}说:我{self.age}岁,我在{self.grade}年级") cmy = Student('cmy', 20, 120, 2) cmy.speak()
最后,有喜欢博主写的内容的伙伴可以收藏加关注哦!