目录

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 = "refuel"self.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,类方法的第一个参数固定的,按照惯例命名为cls@classmethoddef 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)实例方法,类方法和静态方法

定义方法的目的往往就是操作类中的属性(因为如果不操作类属性就和类没关系就可以定义成函数就可以了)。方法里面对实例属相进行操作定义成实例方法,因为有self,self才能访问到实例属性。当与当前对象没有关系,不需要访问实例属性,但是需要访问到类中定义的类属性,定义成类方法。静态方法既没有self,也没有cls,为了提供逻辑上的紧密关联,完全是为了类服务的,既没有用到self,也没有用到cls,就用静态方法

4 魔法方法

魔法方法:定义了一些特殊的方法,这些方法通常不会显式去调用,而是在特定的场合下隐式调用执行的。这些特殊的方法名称都是以双下划线开头,并且以双下划线结尾。

(1)__new__(cls, ……)

创建cls类的对象。通过参数的cls可知,该方法是一个类方法,但是不需要使用装饰器来修饰(@classmethod),该方法的其他参数由构造器传入。该方法中,通常会调用父类的__new__方法来创建对象,并返回。如果该方法返回值为当前类的对象,则会继续调用初始化方法(__init__),传入当前创建的对象,否则,初始化方法不会执行。

该方法通常不需要自行实现,当我们实现该方法时,主要用来完成自定义的对象创建。

(2)__init__(self)

初始化方法,在创建对象时,如果__new__方法创建并返回了当前类型的对象,则会调用该方法,对new创建的对象执行初始化。

(3)__del__(self)

当销毁对象时,调用该方法。

(4)__str__(self)

当调用内建函数str,format或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("对象销毁")# 使用内建函数str,format,print时调用,需要返回一个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 b"byte student class"# 当将对象当成函数调用会执行该方法def __call__(self):print("对象当函数调用")
s = Student()
print(s)
print(str(s))
print(repr(s))
print(bytes(s))
s()
del s

5 动态属性

Python提供了如下的内建函数,用来操作对象的属性。

(1)hasattr(obj,name):判断obj对象中是否存在name指定的属性名,存在返回True,否则返回False。

(2)setattr(obj,name,value):将obj对象的name属性设置为value值。相当于执行如下的操作:obj.name = value。如果name属性存在,则覆盖,如果不存在,则为对象obj新增name属性。

(3)getattr(obj,name):返回obj对象的name属性值,相当于执行如下的操作:obj.name。如果name属性值存在,则返回属性值,如果属性值不存在,返回default参数值,如果属性值不存在,default参数也不存在,则产生AttributeError。

(4)delattr(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())

(3)property

在客户端访问时,公有的方法总不如变量访问那样简便,为了既可以直接访问变量,又能够实现很好的封装,做到信息隐藏,可以使用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.books

7.2 继承

(1)什么是继承

继承体现的是一种一般与特殊的关系。如果两个类A与B,A(苹果)是一种特殊的B(水果),我们就称,特殊的类A继承了一般的类B(苹果继承了水果)。对于一般的类型(水果),我们称为父类,而对于特殊的类型(苹果),我们称为子类。当子类继承了父类,子类就可以继承父类中定义的成员(变量,方法等),就好像在子类中自己定义的一样。继承的语法为:

class B(A):

类体

这样,B类就继承了A类,B就成为一种特殊的A,B类就会继承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中体现的不明显,不像一些定义变量时需要指定明确类型的语言(例如Java,C++等)中那么明显。

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
"""

Python基础(六)--类与对象相关推荐

  1. 刻意练习:Python基础 -- Task10. 类与对象

    背景 我们准备利用17天时间,将 "Python基础的刻意练习" 分为如下任务: Task01:变量.运算符与数据类型(1day) Task02:条件与循环(1day) Task0 ...

  2. Python基础78 - 类和对象魔法方法

    Python的类和对象 类比介绍 要搞懂类和对象,可以以类比到实际生活经验帮助自身充分了解,例如建房子. 类(class):相当于施工图纸(blueprint) 对象(object):房子(已建成的) ...

  3. Python基础(7)——类和对象(一)

    类和对象(一) 1. 基本概念 2. 类的定义和实例化 3. 类中的属性和方法 3.1 属性 3.1.1 类属性 3.1.2 实例属性 3.2 方法 3.3 类的特殊方法 3.3.1 常用特殊方法 3 ...

  4. Python基础 定义类和对象(类和对象2)

    定义简单的类 面向对象是更大的封装,在一个类中封装多个方法,这样通过这个类创建出来的对象,就可以直接调用这些方法了! 定义只包含方法的类 在python中,要定义一个只包含方法的类,语法格式如下: # ...

  5. 二十六. Python基础(26)--类的内置特殊属性和方法

    二十六. Python基础(26)--类的内置特殊属性和方法 ● 知识框架 ● 类的内置方法/魔法方法案例1: 单例设计模式 # 类的魔法方法 # 案例1: 单例设计模式 class Teacher: ...

  6. Python之面向对象类和对象

    Python之面向对象类和对象 定义一个类:class 定义类的语法: class Test(object):"""类里定义一类事物共同的技能.可以是变量,也可是函数.& ...

  7. 自动化测试之python基础什么是面对对象之类和对象

    前言 学习自动化测试需要掌握一门开发语言,Java.python这两个可以选一个,这里呢笔者推荐的是python语言,因为它比较好学,那么今天呢,笔者就来给大家介绍一下python基础中的面对对象之类 ...

  8. Python基础之day08-面向对象基础3

    Python基础之day08-面向对象基础3 文章目录 Python基础之day08-面向对象基础3 一. 二.类方法 三.静态方法 四.总结实例.类.静态方法 代码仓库地址: https://git ...

  9. Python基础之day06-面向对象基础1

    Python基础之day06-面向对象基础1 文章目录 Python基础之day06-面向对象基础1 一.面向对象介绍 二.定义类及创建类对象 三.在类中通过self获取对象属性 四.__init__ ...

  10. Python基础之day07-面向对象基础2

    Python基础之day07-面向对象基础2 文章目录 Python基础之day07-面向对象基础2 一.继承 二.子类调用父类的方法 三.多层继承与多继承 四.多态 五.封装 代码仓库地址: htt ...

最新文章

  1. 完全理解Python迭代对象、迭代器、生成器
  2. jar java classpath_win7中java编程工具安装 java环境变量设置
  3. Android stadio Switch repository Android stadio切换仓库
  4. python编程做什么工作-学习Python编程后在成都可以做哪些工作?
  5. Linux服务器防火墙白名单设置
  6. 调度算法为何被阿里如此重视?
  7. 求一个序列中最大的子序列_最大的斐波那契子序列
  8. 1.3求根之牛顿迭代法
  9. 经济学与计算机学收入,考研心得,计算机专业跨考经济学复习经验谈
  10. ICDAR 2019 论文下载
  11. Android4.0升级新特性
  12. 判断文件是否损坏_Bash技巧:把变量赋值为换行符,判断文件是否以换行符结尾...
  13. 什么是狭义人工智能、通用人工智能和超级人工智能?
  14. Matlab最小二乘系统辨识
  15. python xlrd 过滤excel里的隐藏行
  16. Efficient Protocols for Set Membership and Range Proof 学习笔记
  17. 微信小程序账号注册初始化环境搭建
  18. proteus仿真arduino控制舵机
  19. 京东小白如何快速掌握运营技巧
  20. latex表格内上下边距调整

热门文章

  1. python学习笔记(二十)初识面向对象
  2. UVALive 4394 String painter
  3. 如何用jar命令对java工程进行打包
  4. 【转】[iOS] 关于 self = [super init];
  5. Symbian中不能跨越线程(RThread)使用的对象/组件(RSocket/Memery Heap,etc)
  6. 华为海选开发者状元?还送14件豪礼?
  7. 你知道Linux里D进程会搞事吗?
  8. STM32 LED灯的另一种写法
  9. python cmd命令 循环传参数_将参数从cmd传递给python脚本
  10. 报错curl: (7) Failed to connect to 127.0.0.1 port xxxx: Connection refused