文章目录
python面向对象2
面向对象(继承,多态)
继承
定义
-
当定义一个
class
的时候,可以从某个现有的class继承,新的class称为子类(Subclass
),而被继承的class称为基类、父类或超类(Base class
、Super class
) -
格式:
class 子类名称(基类名称)
class Animal(object):#定义一个名为Animal的基类
def run(self):
print('动物可以跑')
class Dog(Animal): #定义一个名为Dog的子类
pass
class Cat(Animal): #定义一个名为Cat的子类
pass
dogObj = Dog()
catObj = Cat()
dogObj.run()
catObj.run()
# 结果
动物可以跑
动物可以跑
- 对于子类继承的父类的方法,如
run
,如果子类不做特殊处理,那就会具有和父类的一样的被继承的方法。如果子类对被继承的方法做了修改(函数的定义不变,变的只是函数的实现),那么这就是重写方法。- 继承的一个好处就是:多态;允许方法重写是多态的具体体现。
子类指定调用父类的方法
-
在子类的类内,进行指定
super().父类的方法名() 父类名字.父类方法(self) super(当前类的名字,self).父类的方法名()
class Animal(object): # 定义一个名为Animal的基类 def run(self): print('动物可以跑') class Dog(Animal): def run(self): return super().run() #子类指定父类中的方法1 class Cat(Animal): def run(self): return Animal.run(self) #子类指定父类中的方法2 class Fox(Animal): def run(self): return super(Fox, self).run() #子类指定父类中的方法3 dogObj = Dog() catObj = Cat() foxObj = Fox() dogObj.run() catObj.run() foxObj.run() # 结果 动物可以跑 动物可以跑 动物可以跑
-
在子类的类外,无法进行指定
多继承
- Python同样有限的支持多继承形式
- 圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
- 格式:
class 子类名称(基类1名称, 基类2名称...)
class BaseClass1():
def fun(self):
print('这是基类1中的方法')
class BaseClass2():
def fun(self):
print('这是基类2中的方法')
class SubClass(BaseClass1, BaseClass2):#继承顺序为BaseClass1, BaseClass2
pass
subClassObj =SubClass()
subClassObj.fun() #BaseClass1, BaseClass2都有fun()方法,但是SubClass没有进行重写,所以会调用 #BaseClass1中的fun方法
# 结果
这是基类1中的方法
-
多继承中指定基类中方法
父类名字.父类方法(self) super(当前类的名字,self).父类的方法名()
class BaseClass1(): def fun(self): print('这是基类1中的方法') class BaseClass2(): def fun(self): print('这是基类2中的方法') class SubClass(BaseClass1, BaseClass2):#继承顺序为BaseClass1, BaseClass2 def fun(self): return BaseClass2.fun(self) #进行指定基类中的方法 subClassObj =SubClass() subClassObj.fun() #BaseClass1, BaseClass2都有fun()方法,但进行了指定,所以会调用 #BaseClass2的方法 # 结果 这是基类2中的方法
多态
- 继承的一个好处就是:多态;允许方法重写是多态的具体体现。
- 多态的通俗理解:继承自同一父类的不同的子类会重写父类中的某些方法,但他们的方法名是一致的,在调用某些方法时,不同类型的对象会有不同的行为
方法重写
class Animal(object): # 定义一个名为Animal的基类
def run(self):
print('动物可以跑')
class Dog(Animal):
def run(self):
print('这是狗在跑')
class Cat(Animal):
def run(self):
print('这是猫在跑')
class Fox(Animal):
def run(self):
print('这是猫在跑')
dogObj = Dog()
catObj = Cat()
dogObj.run()
catObj.run()
# 结果
这是狗在跑
这是猫在跑
进一步理解
- 对于一个变量,我们只需要知道它是
Animal
类型,无需确切地知道它的子类型,就可以放心地调用run()
方法,而具体调用的run()
方法是作用在Animal
、Dog
、Cat
还是Tortoise
对象上,由运行时该对象的确切类型决定 - 调用方只管调用,不管细节;而当我们新增一种
Animal
的子类时,只要确保run()
方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:- 对扩展开放:允许新增
Animal
子类; - 对修改封闭:不需要修改依赖
Animal
类型的run_twice()
等函数
- 对扩展开放:允许新增
鸭子类型
- 对于静态语言(例如Java)来说,如果需要传入
Animal
类型,则传入的对象必须是Animal
类型或者它的子类,否则,将无法调用run()
方法。因为会做类型检查,类型不符,则会抛异常。 - 对于Python这样的动态语言来说,则不一定需要传入
Animal
类型。我们只需要保证传入的对象有一个run()
方法就可以了
含义
-
只要传入的对象有就行
-
“file-like object“,“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子
class Animal(object): # 定义一个名为Animal的基类
def run(self):
print('动物可以跑')
def run_twice(Animal):
Animal.run()
Animal.run()
class MyClass():
def run(self):
print('这不是一个Animal的run,这是MyClass中的run')
myclassObj = MyClass()
run_twice(myclassObj) #myclassObj就像只鸭子,它有run_twice参数中Animal的run方法,那么就运行 #Myclass中的run方法
#至于run_twice运行的是不是Animal的run不管,只要传入的参数有run方法就行
# 结果
这不是一个Animal的run,这是MyClass中的run
这不是一个Animal的run,这是MyClass中的run
获取对象信息(type,isinstance,dir)
type
- 判断基本数据类型
print(type('a'))
# 结果
<class 'str'>
-
使用
types
模块import types def fun(): pass print(type(fun) == types.FunctionType) print(types.FunctionType) # 结果 True <class 'function'>
isinstance
-
格式:
isinstance(变量,类型名)
,返回值是布尔型print(isinstance(1, int)) print(isinstance(1, str)) # 结果 True False
- 总是优先使用isinstance()判断类型
dir
-
如果要获得一个对象的所有属性和方法,可以使用
dir()
函数,它返回一个包含字符串的listprint(dir('a')) # 结果 ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
-
通过内置的一系列函数,我们可以对任意一个Python对象进行剖析,拿到其内部的数据。要注意的是,只有在不知道对象信息的时候,我们才会去获取对象信息。
-
举例
def readImage(fp): if hasattr(fp, 'read'): return readData(fp) return None
- 假设我们希望从文件流fp中读取图像,我们首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。
hasattr()
就派上了用场 - 根据鸭子类型,有
read()
方法,不代表该fp对象就是一个文件流,它也可能是网络流,也可能是内存中的一个字节流,但只要read()
方法返回的是有效的图像数据,就不影响读取图像的功能
- 假设我们希望从文件流fp中读取图像,我们首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。
-