好看英文网站,专业的网站建设公司排名,中国建设信息网站,昊杰南宫网站建设目录 Python基础#xff08;六#xff09;--类与对象
1 类与对象的基本概念
1.1 什么是对象
1.2 什么是类
1.3 类与对象的关系
2 定义与初始化
2.1 类的定义
2.2 对象的初始化
2.3 动态增加属性方法
3 类成员
3.1 类属性与实例属性
3.2 类方法与实例方法
3.3 静态…目录 Python基础六--类与对象
1 类与对象的基本概念
1.1 什么是对象
1.2 什么是类
1.3 类与对象的关系
2 定义与初始化
2.1 类的定义
2.2 对象的初始化
2.3 动态增加属性方法
3 类成员
3.1 类属性与实例属性
3.2 类方法与实例方法
3.3 静态方法
3.4 类与实例
4 魔法方法
5 动态属性
6 面向对象与面向过程
7 面向对象的三大特征
7.1 封装
7.2 继承
7.3 多态 Python基础六--类与对象
1 类与对象的基本概念
1.1 什么是对象
对象具有属性和行为属性多体现为名词而行为多体现为动词。
1.2 什么是类
类其实就是指一个类别具有相同属性与行为的所有对象构成的一个整体。
1.3 类与对象的关系
1类是对象的抽象而对象是类的具体表现形式
2类是设计的模板而对象是该模板设计出的具体产物
2 定义与初始化
2.1 类的定义
使用class关键字定义类如下
class 类名 类体
想让对象具备那些功能属性和行为就需要在类中指出。就需要在现实与Python程序中进行一种映射对象的属性通过令对象绑定一个变量来实现而对象的行为通过在类内定义方法来实现。所谓方法其形式与函数非常相似只不过是定义类的内部关联了某个对象而已。
self在两个方法中都具有一个参数self。该参数用来表示当前的对象调用该方法的时候所使用的对象。简单说就是谁调用了这个方法当前对象就是谁。
如果仅仅定义了类而没有为类具体化创建对象是不能够使用类中定义的功能的通过对象调用方法时该对象就会隐式的传递给方法的第一个参数无需我们显式传递。
# 定义一个学生类
class Student:def study(self):print(学习)
# 通过类创建对象
s Student()
# 通过对象访问类中的成员
s.study()
# 为对象增加属性有则修改无则添加
s.name refuel
s.age 18
print(s.name,s.age)方法与函数的区别①方法就是函数只不过函数是面向过程的称呼而方法是面向对象的称呼②函数不依赖类对象而方法依赖于一个类的对象
2.2 对象的初始化
在类中定义了方法来表示对象的行为对象也是可以具有属性的。例如人可以具有姓名年龄等属性。定义属性我们可以在__init__中进行定义
1__init__方法
__init__方法会在创建对象时自动得到执行。并且每创建一个对象该方法都会执行一次。可以在该方法中执行对象的初始化定义属性就是一件最常用的操作。而__init__方法具有初始化的能力我们也习惯将其称为构造器或构造方法。
2含有参数的__init__方法
不含参的__init__方法的一个不足就是无论我们创建多少个对象对象的属性都是完全一致的。为了能够更加灵活的创建对象让对象具有不同的属性值我们可以在__init__方法中增加参数然后在创建对象时动态传递实际参数来初始化对象的属性不再使用固定值初始化这样就能够避免创建属性值完全相同的对象了。
# 对象的初始化
# self表示当前的对象__init__(self)中的self指我们创建的对象对于其他方法当前对象只调用该方法的那个对象
class Student:# __init__方法在创建对象的时候会自动执行可以在该方法中为当前创建的对象增加属性def __init__(self):self.name refuelself.age 18def study(self):print(学习)
s Student()class Student2:# 在定义__init__方法的时候可以为该方法提供一些参数来更加灵活的进行初始化就可以避免千篇一律的创建对象def __init__(self,name,age):self.name nameself.age agedef study(self):print(学习)
s2 Student2(refuel,18) 2.3 动态增加属性方法
除了在类中预先定义属性与方法外我们也可以动态的为对象增加属性与方法。】
1动态增加的方法在调用时需要显式的传递当前对象。
2动态增加的属性与方法仅对当前对象有效对于其他对象是无效的。
# 动态增加属性与方法仅对当前对象有效对其他对象无效
class Student:pass
def study(self):print(动态增加的方法)
s Student
s.name 动态增加的属性
s.study study
print(s.name)
# 动态增加的方法需要显示的传递self对象
s.study(s) 3 类成员
3.1 类属性与实例属性
1实例属性定义的属性都会与类的对象相关联的。对象也就是实例创建类的对象也可以称为创建类的实例。
2类属性定义的属性与当前类进行关联不属于对象直接定义在类体中。
3实例属性与类属性的区别
①属性的绑定不同类属性与类进行绑定与对象无关可以共享给所有对象访问。实例属性与对象绑定每个对象有自己的实例属性不影响其他对象
②访问方式不同类属性可以通过类访问也可以通过对象访问。实例属性只能通过对象访问而不能通过类访问。通过对象访问类属性仅限于访问无法修改类属性的值尝试修改的话只不过是动态的为当前对象新增一个实例属性这个实例属性与类属性同名。
类属性与实例属性名称相同时不会产生错误。通过类访问的是类属性通过对象访问的可能是类属性也可能是实例属性。此时要分为读取属性的值还是修改属性的值。当读取属性值时首先尝试访问同名的实例属性如果实例属性不存在则会访问类属性如果类属性也不存在则会产生错误。当修改属性值时如果实例属性存在则修改如果实例属性不存在则新增该实例属性即通过对象修改属性时操作的属性一定是实例属性。
注意最好使用类名访问类属性不要通过对象访问类属性避免造成不必要的混淆
# 实例属性每个对象独享彼此不受干扰类属性对象共享
# 类属性与实例属性的区别①属性的绑定不同类属性绑定的是当前类与当前类的任何对象都无关实例属性绑定的是当前对象
# ②访问方式不同类属性可以通过类名访问也可以通过对象进行访问只读因为类属性共享给所有对象。实例属性只能通过对象访问不能通过类名访问
class Student:# 类属性stu 学生def __init__(self,name):# 实例属性self.name name
# 类名访问类属性
print(Student.stu)
# 对象访问类属性
s Student(refuel)
print(s.stu)
# 注意通过类名是不能访问实例属性的也就是不能Student.name
# 对象无法修改类属性只能访问所以当名称和类属性一样时仅仅是为当前对象增加一个实例属性。修改类属性可以通过类名修改
s.stu 学生-修改
print(s.stu)
print(Student.stu)
# 实例属相是对象独享的彼此不受干扰
s2 Student(张三)
s3 Student(李四)
print(s2.name,s3.name) 3.2 类方法与实例方法
1类方法
使用classmethod修饰的方法这样的方法称为类方法。
按照惯例并非语法要求将类方法的第一个参数命名为cls这类似于实例方法的第一个参数self该参数是有特殊意义的用来表示当前类即调用该类方法的那个类。在调用类方法时cls无需我们显式传递就像实例方法的self无需我们显式传递一样。
类方法的作用类方法与当前类绑定与类的对象无关因此我们通常使用类方法来作为工厂方法用来创建并返回类的对象这就相当于提供了另外一种构造器。
2实例方法
实例方法与具体的类的某个对象想关联
类方法与实例方法二者既可以通过类名调用也可以通过对象来调用。不过就像通过对象访问类属性一样通过对象来调用类方法会造成不必要的混淆类方法与类的对象无关是绑定当前类的因此我们应该总是通过类名来调用类方法。
3类方法与实例烦啊对属性的访问
在类方法中需要通过参数cls访问类属性而不能直接访问类属性。实例方法没有cls参数可以通过类名来访问类属性但是这是一种硬编码的方式如果以后类名发生了改变则我们通过类名访问类属性的语句都需要进行修改。这不利于程序的维护。为了降低耦合性我们在实例方法中可以通过__class__来动态获取当前对象所属的类
self.__class__ 相当于获取到了cls也就是self对象所属的类动态获取如果是在类的内部我们还可以省略对象的限定直接使用__class__ 那么访问方式就可以改为 类的对象.__class__.类属性__class__.类属性 必须在类的内部。这种访问的优势在于以后就算类名发生了改变访问类属性的语句也无需进行任何修改利于程序的维护。
class Student:# 实例方法def study(self):print(实例方法)# 类方法使用classmethod类方法的第一个参数固定的按照惯例命名为clsclassmethoddef clsmethod(cls):print(类方法)
# 类方法的第一个参数会隐式传递
Student.clsmethod()
# 通过对象可以调用实例方法也可以调用类方法。虽然对象可以访问类方法但是不建议这么做
s Student()
s.clsmethod()
s.study()
# 通过类名可以访问类方法也可以访问实例方法需要显示传递对象
Student.study(s)
print(--------------------------------)
# 类方法与实例方法访问属性
class Person:# 类属性intro 类属性def __init__(self,name,age):# 实例属性self.name nameself.age age# 类方法classmethoddef eat(cls):# 类方法访问类属性cls.intro 类方法修改# 类方法不能访问实例属性如果一定要访问那就要传入一个对象进行访问但是如果这样那就应该定义成实例方法# 实例方法def jump(self):# 实例方法访问实例属性print(self.name)# 实例方法访问类属性print(self.intro)# 获取对象所属的类 self.__class__print(self.__class__.intro)# Python不支持方法的重载可以用类方法来返回一个对象相当于提供了一种不同的构造器来创建对象classmethoddef copy(cls,p):return cls(p.name,p.age)
Person.eat()
print(Person.intro)
p Person(refuel,18)
p.jump()
p2 Person.copy(p)
print(p2.name,p2.age) 3.3 静态方法
静态方法使用staticmethod修饰
静态方法没有cls参数。对于静态方法而言其既与当前类的对象无关也与当前类无关可以说静态方法只是定义在类的内部从逻辑结构上与当前类划分到了一起从功能的角度讲我们完全可以将静态方法迁移到类的外部作为函数来实现同样的功能。
如果某个函数其实现的功能与某个类关系紧密但是又不依赖于当前类cls与当前对象self时我们就可以将该函数声明在类的内部作为静态方法。静态方法既可以通过类名调用也可以通过对象调用建议通过类名调用因为静态方法与对象无关。
class Student:def __init__(self,name):self.name name# 静态方法使用staticmethod修饰既没有self也没有cls所以# 静态方法的设计原则为既不依赖当前的类属性也不访问当前对象的实例属性# 从结构上看可以将静态方法移除到类外就像函数一样但是# 静态方法定义在类的内部的意义为有些方法完全为一个类服务具有逻辑上的关联性staticmethoddef statmethod():print(静态方法)staticmethoddef hand_in_homework(s):print(f{s.name}交作业)
Student.statmethod()
s Student(refuel)
# 虽然对象可以访问静态方法但是静态方法与当前对象没有关系我们应该总是通过类名来访问静态方法
s.statmethod()
# 上交作业的函数既没有用到类属性也没有使用实例属性虽然用到name是对象传过来的不是用self访问的
# 但是该函数的功能是完全为Student类服务的因此可以定义在Student方法中成为静态方法
def hand_in_homework(s):print(f{s.name}交作业)Student.hand_in_homework(s) 3.4 类与实例
我们在类中可以定义类属性类方法静态方法也可以定义实例属性与实例方法。在设计一个类的时候该如何进行选择呢
1对于类属性和实例属性
看属性是所有对象共享的类属性如人有几根手指还是单独一个对象独享的实例属性如人的名字年龄
2实例方法类方法和静态方法
定义方法的目的往往就是操作类中的属性因为如果不操作类属性就和类没关系就可以定义成函数就可以了。方法里面对实例属相进行操作定义成实例方法因为有selfself才能访问到实例属性。当与当前对象没有关系不需要访问实例属性但是需要访问到类中定义的类属性定义成类方法。静态方法既没有self也没有cls为了提供逻辑上的紧密关联完全是为了类服务的既没有用到self也没有用到cls就用静态方法
4 魔法方法
魔法方法定义了一些特殊的方法这些方法通常不会显式去调用而是在特定的场合下隐式调用执行的。这些特殊的方法名称都是以双下划线开头并且以双下划线结尾。
1__new__(cls, ……)
创建cls类的对象。通过参数的cls可知该方法是一个类方法但是不需要使用装饰器来修饰classmethod该方法的其他参数由构造器传入。该方法中通常会调用父类的__new__方法来创建对象并返回。如果该方法返回值为当前类的对象则会继续调用初始化方法__init__传入当前创建的对象否则初始化方法不会执行。
该方法通常不需要自行实现当我们实现该方法时主要用来完成自定义的对象创建。
2__init__(self)
初始化方法在创建对象时如果__new__方法创建并返回了当前类型的对象则会调用该方法对new创建的对象执行初始化。
3__del__(self)
当销毁对象时调用该方法。
4__str__(self)
当调用内建函数strformat或print时就会调用对象的该方法。该方法必须返回字符串str类型用来描述对象的字符串表示。
5__repr__(self)
当调用内建函数repr时就会调用对象的该方法。该方法必须返回字符串str类型用来描述对象的字符串表示。如果类中定义了该方法但是没有定义__str__方法则当需要调用__str__方法时也会调用该方法代替。
6__bytes__(self)
当调用内建函数bytes时会调用该方法该方法必须返回字节bytes类型用来以字节的形式描述对象。
7__call__(self)
当把类的对象作为方法调用时就会调用该方法。
# 魔法方法就是使用__方法名__的命名规则通常情况下不会主动调用在满足一定条件下自动调用
class Student:# __new__是一个不使用classmethod修饰的类方法在创建对象时首先会调用这个方法返回当前类的对象# 如果该方法返回当前类的对象那么接下来会调用__init__方法对对象进行初始化否则__init__方法不会得到执行def __new__(cls):print(new执行)# return cls()不能这么做会无限递归# 通过super调用__new__就不会出现无限递归return super().__new__(cls)# 初始化方法在创建对象时如果__new__方法创建并返回了当前类型的对象则会调用该方法对new创建的对象执行初始化。def __init__(self):print(init方法)# 当前对象销毁时调用这个方法可以执行一些清理的工作def __del__(self):print(对象销毁)# 使用内建函数strformatprint时调用需要返回一个str类型的对象def __str__(self):return Student类型对象# 在使用内建函数repr时调用需要返回一个str类型的对象# __str__与__repr__的区别都返回字符串__str__返回的是让人容易阅读的格式__repr__通常返回# 的是面向Python解释器的。当需要调用__str__的场合没有定义__str__就是要__repr__替代def __repr__(self):return Student class# 当使用内建函数bytes时调用返回字节类型def __bytes__(self):return bbyte student class# 当将对象当成函数调用会执行该方法def __call__(self):print(对象当函数调用)
s Student()
print(s)
print(str(s))
print(repr(s))
print(bytes(s))
s()
del s 5 动态属性
Python提供了如下的内建函数用来操作对象的属性。
1hasattr(obj,name)判断obj对象中是否存在name指定的属性名存在返回True否则返回False。
2setattr(obj,name,value)将obj对象的name属性设置为value值。相当于执行如下的操作obj.name value。如果name属性存在则覆盖如果不存在则为对象obj新增name属性。
3getattr(obj,name)返回obj对象的name属性值相当于执行如下的操作obj.name。如果name属性值存在则返回属性值如果属性值不存在返回default参数值如果属性值不存在default参数也不存在则产生AttributeError。
4delattr(obj,name,value)删除obj对象的name属性。相当于执行如下的操作del obj.name.如果指定的属性不存在则会产生AttributeError。
如果使用obj.name的方式访问属性的话name必须是编写代码时已知的内容而不能通过字符串来指定。以上函数的优势在于我们可以通过字符串的形式来操作属性而无需在编写代码的时候就知道具体的属性名称。因此这就给我们操作属性带来了一定的灵活性。我们完全可以在运行时以字符串的形式传递属性名进行操作。
# 魔法方法就是使用__方法名__的命名规则通常情况下不会主动调用在满足一定条件下自动调用
class Student:pass
s Student()
# 参数1对象参数2属性名 判断对象中是否存在第二个参数指定的属性名存在返回True否则False
print(hasattr(s,name))
# 参数1对象参数2属性名 返回对象对应的第二个参数指定的属性不存在产生异常可以指定第三参数不存在时返回的默认值
print(getattr(s,name,没属性的默认值))
# 参数1对象参数2属性名 参数三属性值
print(setattr(s,age,18))
delattr(s,age) 6 面向对象与面向过程
编程方式可分为面向对象和面向过程。
面向过程体现的是一种流程设计即按部就班的完成每一个环节。为了实现每个环节的可复用性环节都是通过调用一个个函数来实现的。因此在面向过程的编程中通常都是定义大量的函数按照需求顺序依次进行调用。
面向对象程序不再是以函数为单位而是以对象为单位通过调用对象提供的方法处理。
7 面向对象的三大特征
7.1 封装
1什么是封装
封装隐藏具体的实现细节只提供给外界调用的接口。只要提供给外界的接口不变即可底层细节改变的时候不会对外界造成影响。
2私有成员
在程序中可以通过变量私有化做封装使得在类中定义的变量仅能在当前类定义变量的类中访问而不能在类的外部访问。如果一个属性名或方法名使用两个下划线__开头并且少于两个下划线结尾则这样的属性方法就称为私有属性方法。私有属性方法只能在类的内部访问。
注意其他语言定义成私有了是真正访问不到但是python的私有只不过是进行了伪装而已。当在类中定义私有成员时在程序内部会将其处理成_类名 原有成员名称的形式。也就是会将私有成员的名字进行一下伪装而已如果使用处理之后的名字还是能够进行访问的。
# 私有变量以__开头但是不能以两个或更多的_进行结尾
class Student:def __init__(self):self.desc 学生# 私有成员私有实例属性self.__books 5# 在定义成员类的内部可以访问私有成员print(self.__books)# 提供公有方法访问私有的属性def set_books(self,books):self.__books books# 提供公有反复噶获取私有的属性def get_books(self):return self.__books
s Student()
print(s.desc)
# print(s.__books) AttributeError: Student object has no attribute __books 私有成员不能在类外进行访问
# 通过类提供的公有方法访问私有成员
s.set_books(10)
print(s.get_books()) 3property
在客户端访问时公有的方法总不如变量访问那样简便为了既可以直接访问变量又能够实现很好的封装做到信息隐藏可以使用property的两种方式来实现封装。
property有两种方式来实现封装①使用property函数②使用property装饰器
# 使用property定义一个属性伪装成一个属性
# 使用property既可以提供访问的便利性也可以提供很好的封装性
# 1使用property函数
class Student:def __init__(self):self.__books 2# property的4个参数# get方法在读取property值的时候调用# set方法在修改property值的时候调用# del方法在删除property的时候调用# doc给出该property的说明文档def getBooks(self):print(调用get方法)return self.__booksdef setBooks(self,books):print(调用set方法)self.__books booksdef delBooks(self):print(调用del方法)del self.__booksbooks property(getBooks,setBooks,delBooks,书本的数量)
s Student()
print(s.books)
s.books 10
print(s.books)
del s.books
print(----------------------)
# 2 使用property装饰器
class Student2:def __init__(self):self.__books 2# 将方法名伪装成一个属性获取属性会调用说明文档也写在该方法中propertydef books(self):books的说明文档return self.__books# 设置属性会调用该方法books.setterdef books(self,books):self.__books books# 删除属性调用该方法books.deleterdef books(self):del self.__books
s2 Student()
print(s2.books)
s2.books 10
print(s2.books)
del s2.books7.2 继承
1什么是继承
继承体现的是一种一般与特殊的关系。如果两个类A与BA苹果是一种特殊的B水果我们就称特殊的类A继承了一般的类B苹果继承了水果。对于一般的类型水果我们称为父类而对于特殊的类型苹果我们称为子类。当子类继承了父类子类就可以继承父类中定义的成员变量方法等就好像在子类中自己定义的一样。继承的语法为
class B(A) 类体
这样B类就继承了A类B就成为一种特殊的AB类就会继承A类的成员。
如果没有显式指定继承的类型则类隐式继承object类object是Python中最根层次的类所有类都是object的直接或间接子类。
# 通过继承可以将公共的功能提取出来放入父类中然后每一个子类去继承父类
# 那就无需将公共的功能在子类中分别实现从而实现代码的重用
class Animal:def desc(self):print(动物)def eat(self):print(动物需要进食)
# 继承定义类是指定继承的父类
class cat(Animal):# 子类重写父类的功能def desc(self):print(我是一只猫)# 子类增加自己特有的方法def catchMouse(self):print(抓老鼠)
a Animal()
a.desc()
c cat()
c.desc()
c.catchMouse() 2为什么要使用继承
我们有时候可能还需要对现有类进行调整这体现在①现有类的提供的功能不充分我们需要增加新的功能。②现有类的提供的功能不完善或对我们来说不适合我们需要对现有类的功能进行改造就是重写。
3成员的继承
子类可以继承父类的成员父类中声明的类属性、实例属性、类方法、实例方法与静态方法子类都是可以继承的。但是对于实例属性有些特别。因为实例属性是定义__init__方法中实现与对象self的绑定。如果子类没有定义init方法就会继承父类的init方法从而在创建对象时调用父类的init方法会将子类对象传递到父类的init方法中从而实现子类对象与父类init方法中实例属性的绑定。但是如果子类也定义了自己的init方法重写则父类init方法就不会得到调用这样父类init方法中定义的实例属性就不会绑定到子类对象中。
如果子类与父类的初始化方式完全相同子类只要继承父类__inir__方法就可以的。但有的时候子类可能会增加自己的属性此时就不能完全套用父类的初始化方式。
虽然父类的__init__不完全适合子类但是也并非完全不适合子类。因为两个类还是存在相同的属性的因此我们应该充分利用现有的功能不要重复的实现。实现方式就是我们在子类的构造器中去调用父类的构造器完成公共属性的初始化然后在子类构造器中再对子类新增属性进行初始化。我们可以这样来调用父类的构造器 super().__init__(父类构造器参数)
# 成员的继承
class Animal:classAttr 6def __init__(self):self.instanceAttr 2def instanceMethod(self):print(实例方法)classmethoddef classMethod(cls):print(类方法)staticmethoddef staticMethod():print(静态方法)# 私有成员是可以被子类继承的不过继承的不是原来的名字而是伪装之后的名字def __pri(self):print(私有方法)
class cat(Animal):# 父类的初始化不完全适用于子类子类定义自己的初始化方法def __init__(self,name,age):#调用父类的__init__方法super().__init__()self.name nameself.age agedef private(self):# 继承私有成员self._Animal__pri()
c cat(aa,18)
print(cat.classAttr)
print(c.instanceAttr,c.name,c.age)
cat.classMethod()
c.instanceMethod()
cat.staticMethod()
c.private() 4继承的两个内建函数isinstance与issubclass
class Animal:pass
class Cat(Animal):pass
a Animal()
c Cat()
# instance(o,t)参数一对象参数二类型。判断第一个指定的参数是否是第二个参数类型包括父类型
print(isinstance(a,Animal))
print(isinstance(c,Cat))
print(isinstance(c,Animal))
print(isinstance(c,object))
print(isinstance(a,Cat))
print(------------)
# issubclass()判断第一个参数类型是否是第二个参数的类型或者是其子类型
print(issubclass(Cat,Cat))
print(issubclass(Cat,Animal))
print(issubclass(Animal,Cat))
print(issubclass(Animal,Animal))
print(issubclass(Cat,object)) 5重写
当子类继承了父类子类就可以继承父类的成员。然而父类的成员未必完全适合于子类例如鸟会飞但是鸵鸟不会飞此时子类就将父类中的成员进行调整以实现适合子类的特征与功能。我们将父类中的成员在子类中重新定义的现象称为重写。当通过子类对象访问成员时如果子类重写了父类的成员将会访问子类自己的成员。否则没有重写访问父类的成员。
如果子类需要访问父类的成员可以通过super().父类成员 进行访问。
class Animal:def eat(self):print(进食)
class Cat(Animal):def eat(self):super().eat()print(吃鱼)
c Cat()
c.eat() 6多重继承
在Python中类是支持多重继承的即一个子类可以继承多个父类。java只能单继承。
当子类继承多个父类时子类会继承所有父类的成员。当多个父类含有相同名称的成员时我们可以通过具体的父类名来指定要调用哪一个父类的成员如果是实例方法需要显式传递一个类对象这样就能够避免混淆。
同名的访问由Python中的方法解析顺序MRO来决定访问某个类的成员时成员的搜索顺序。该顺序大致如下①如果是单继承继承一个父类则比较简单搜索顺序从子类到父类一直到object为止。②如果是多继承继承多个父类则子类到每个父类为一条分支按照继承的顺序沿着每一条分支从子类到父类进行搜索一直到object类为止深度优先。③在搜索的过程中子类一定会在父类之前进行搜索。 成员的搜索顺序为F - D - B - E - C - A - object
# 在python中一个类可以继承多个父类
class Animal:def desc(self):print(动物)class Felid:def desc(self):print(猫科动物)
# 猫同时继承两个类型
class Cat(Animal,Felid):def meth(self):self.desc()
c Cat()
c.meth() 成员搜索顺序之前使用的super类就是根据方法解析顺序来查找指定类中成员但是有一点例外就是super对象不会在当前类中搜索即从方法解析顺序的第二个类开始。super的构造器会返回一个代理对象该代理对象会将成员访问委派给相关的类型父类或兄弟类。
7方法解析顺序MRO
每个类都提供了mro方法与__mro__属性可以获得该类的方法解析顺序。mro方法与__mro__属性获取的元素内容是相同的只不过前者返回的是列表list类型后者返回的是元组tuple类型。
class A:x A
class B(A):x B
class C(A):x C
class D(B):x D
class E(C):x E
class F(D,E):x F
# 获取方法的解析顺序
print(F.mro())
print(F.__mro__)
# super()返回一个委派对象通过委派对象访问类中的成员委派的顺序
# 与mro的顺序是一致的除了排除自身 7.3 多态
多态指运行时表现的多种形态运行时会根据对象的真正类型来决定调动哪一个类型的成员。
因为Python是鸭子类型因此多态在Python中体现的不明显不像一些定义变量时需要指定明确类型的语言例如JavaC等中那么明显。
class Father:def talk(self):print(father)
class Son:def talk(self):print(son)
def talk(m):m.talk()
f Father()
s Son()
talk(f)
talk(s)其他指定类型的语言
Father f new Father()
Son s new Son()
talk(f)
talk(s)
def talk(Father f)f.talk