关于什么是面向对象及相关的一些概念不在本篇的说明范围,以下是关于python中类的使用方式的说明。
目录
■ 方法搜索顺序 MRO(Method Resolution Order)
语法格式:
- class 类名:
-
- def 方法1(self,参数列表):
- pass
-
- def 方法2(self,参数列表:
- pass
注意:
类名的命名规则要符合大驼峰命名法:每个单词的首字母大写;
类中的方法定义时,第一个参数必须是self;
语法格式:
对象变量 = 类名()
定义一个类Rectangle;
定义两个方法,length()表示计算周长,area()表示计算面积;
不定义属性;
- class Rectangle:
-
- def length(self):
- print("计算周长")
-
- def area(self):
- print("计算面积")
-
- if __name__ == '__main__':
- rect = Rectangle()
- rect.length()
- rect.area()
显示结果:
- 计算周长
- 计算面积
方法中的self参数类似C++类中的this。类封装的方法内部,self表示当前调用方法的对象自己。
如上示例,调整需要在一个函数cal()中计算面积和周长,那么可以把代码调整为:
- class Rectangle:
-
- def length(self):
- print("计算周长")
-
- def area(self):
- print("计算面积")
-
- def cal(self):
- self.area()
- self.length()
-
- if __name__ == '__main__':
- rect = Rectangle()
- rect.cal()
显示结果:
- 计算面积
- 计算周长
python提供了以下内置函数,在创建类对象或销毁时会被自动调用。
| 方法名 | 类型 | 作用 |
| __new__ | 方法 | 用来创建对象,会被自动调用 |
| __init__ | 方法 | 对象初始化操作,会被自动调用 |
| __del__ | 方法 | 对象从内存中销毁前操作,会被自动调用 |
| __str__ | 方法 | 返回对象描述信息(字符串类型),可以使用print函数输出 |
__init__和__del__的应用场景:
使用 类名() 的形式创建对象时,python首先会调用new方法为对象分配空间,new方法是object基类提供的内置静态方法,主要的作用有:
python获得new返回的对象的引用后,将引用作为第一个参数,传递给__init__方法。
重写__new__方法的代码是固定的:
需要注意:__new__是一个静态方法,在调用时需要主动传递cls参数。
示例:单例模式设计时,用到该方法。
- class MusicPlayer(object):
-
- instance = None
-
- def __new__(cls,*args,**kwargs):
- if cls.instance is None:
- cls.instance = super().__new__(cls)
- return cls.instance
-
- def __init__(self):
- print("初始化音乐播放器")
-
- if __name__ == '__main__':
- player = MusicPlayer()
- player1 = MusicPlayer()
- print(player)
- print(player1)
显示结果:
- 初始化音乐播放器
- 初始化音乐播放器
- <__main__.MusicPlayer object at 0x000001D5A65C4220>
- <__main__.MusicPlayer object at 0x000001D5A65C4220>
当创建对象时,会自动执行一下操作:
初始化方法就是__init__方法,__init__是对象的内置方法。
该函数不需要外部调用,创建对象时会自动创建。
个人理解 __new__+__init__()函数类似C++中类的构造函数。
专门用来定义一个类的属性。可以在__init__方法内部使用 self.属性名 = 属性的初始值 来定义属性。定义属性之后,创建的对象都拥有该属性。
如下示例:
- class Rectangle:
-
- def __init__(self):
- print("初始化")
- self.width = 4
-
- if __name__ == '__main__':
- rect = Rectangle()
- rect1 = Rectangle()
- print(rect.width)
- print(rect1.width)
显示结果:
- 初始化
- 初始化
- 4
- 4
■ 初始化的同时设置初始值
如果希望创建对象的同时,就设置对象的属性,可以改造__init__方法
个人理解 这种方式类似于 创建一个有参数列表的构造函数。
使用示例:
- class Rectangle:
- def __init__(self,width,height):
- print("初始化")
- self.width = width
- self.height = height
-
- def length(self):
- print("计算周长")
- return (self.width + self.height)*2
-
- def area(self):
- print("计算面积")
- return self.width*self.height
-
-
- if __name__ == '__main__':
- rect = Rectangle(3,5)
- rect1 = Rectangle(6,7)
- print(rect.length())
- print(rect.area())
- print(rect1.length())
- print(rect1.area())
显示结果:
- 初始化
- 初始化
- 计算周长
- 16
- 计算面积
- 15
- 计算周长
- 26
- 计算面积
- 42
当一个对象被从内存中销毁前,会自动调用__del__方法。
对象的生命周期:
使用示例:
- class Rectangle:
- def __init__(self,width,height):
- print("初始化")
- self.width = width
- self.height = height
-
- def length(self):
- print("计算周长")
- return (self.width + self.height)*2
-
- def area(self):
- print("计算面积")
- return self.width*self.height
-
- def __del__(self):
- print("结束")
-
-
- if __name__ == '__main__':
- rect = Rectangle(3,5)
- print(rect.length())
- del rect # del关键字删除变量
- print(rect.area())
-
- print("="*50)
-
运行时会提示 NameError: name 'rect' is not defined,这是因为在打印面积前已经把rect对象删除了。
python中,如果使用print(对象),默认情况下,会输出这个对象是由哪个类创建的,以及在内存中的地址,如下:
- class Rectangle:
- def __init__(self):
- print("初始化")
-
- def __del__(self):
- print("结束")
-
-
- if __name__ == '__main__':
- rect = Rectangle()
- print(rect) # 输出 <__main__.Rectangle object at 0x0000025E54966BB0>
如果在开发中,希望使用 print(对象)时,能够打印自定义的内容,可以使用 __str__ 方法。但是需要注意的是 __str__ 方法必须返回一个字符串。
示例:
- class Rectangle:
-
- def __init__(self):
- print("初始化")
-
- def __str__(self):
- return "这是一个矩形类"
-
- def __del__(self):
- print("结束")
-
-
- if __name__ == '__main__':
- rect = Rectangle()
- print(rect) # 输出 这是一个矩形类
在定义属性或方法时,在属性名或者方法名前加两个下划线,就表示该方法或属性是私有属性或方法。如定义 self.__name,__name就是私有属性;如self.__get_length(),__get_length()就是私有方法。
- class Rectangle:
-
- def __init__(self):
- print("初始化")
- self.__name = '矩形'
-
- def __str__(self):
- return "这是一个矩形类"
-
- def __length(self):
- print('计算矩形周长')
-
- def __del__(self):
- print("结束")
-
-
- if __name__ == '__main__':
- rect = Rectangle()
- print(rect.__name)
- print(rect.__length())
-
运行到 print(rect.__name) 时,会报错:AttributeError: 'Rectangle' object has no attribute '__name',因为__name是私有属性,外部是不能访问的,所以会提示没有__name属性。
■ 要在外部访问私有属性和私有方法,可以使用 对象._类名__属性/方法名的方式访问,但不推荐使用该种方法。
- class Rectangle:
-
- def __init__(self):
- print("初始化")
- self.__name = '矩形'
-
- def __str__(self):
- return "这是一个矩形类"
-
- def __length(self):
- print('计算矩形周长')
-
- def __del__(self):
- print("结束")
-
-
- if __name__ == '__main__':
- rect = Rectangle()
- rect._Rectangle__length()
-
显示结果:
初始化
计算矩形周长
结束
面向对象的三大特性:封装、继承、多态。
简单来说,继承是:子类拥有父类的所有方法和属性。如下列的类,不使用继承时,每个类中都有相同的方法名;使用继承后,子类可以使用父类的方法。
使用继承的情况下,CAnimal是CDog和CCat的父类,CDog和CCat是CAnimal的子类,CDog和CCat从CAnimal类继承;父类又称为基类,子类又称为派生类。


继承使用示例:
- # 基类
- class CAnimal:
- def __init__(self):
- print("父类:Animal 初始化")
-
- def eat(self):
- print("eating...")
-
- def drink(self):
- print("drinking...")
-
- # 派生类
- class CMouse(CAnimal):
- def __init__(self):
- print("子类:CDog 初始化")
-
- def steal(self):
- print("OMG,the mouse is steal rice.")
-
- # 派生类
- class CCat(CAnimal):
- def __init__(self):
- print("子类:CCat 初始化")
-
- def catch(self):
- print("The cat is watching the mouse and prepare catch it.")
-
- if __name__ == "__main__":
- ani = CAnimal()
- jerry_mouse = CMouse()
- tom_cat = CCat()
- jerry_mouse.eat() # 使用的是基类中的eat()方法
- jerry_mouse.drink() # 使用的是基类中的drink()方法
- tom_cat.catch()
显示结果:
- 父类:Animal 初始化
- 子类:CDog 初始化
- 子类:CCat 初始化
- eating...
- drinking...
- The cat is watching the mouse and prepare catch it.
■ 需要注意的是:
子类不能访问父类的私有属性和方法,父类的私有属性和方法仅可以父类的对象或方法访问。
根据基类的个数可以分为单继承和多继承。只继承自一个基类称为单继承,继承自多个基类称为多继承。
单继承和多继承的语法格式:
- # 单继承
- class 类名(父类名):
- pass
-
- # 多继承
- class 类名(父类名1,父类名2,...):
- pass
多继承时需要注意:应该避免不同的父类中存在同名的方法或属性。
python针对类提供了内置的属性 __mro__,可以查看方法的搜索顺序,主要用于在多继承时判断方法、属性的调用路径。
示例:
- class CAnimal:
- def __init__(self):
- print("父类:Animal 初始化")
-
-
- class CMouse(CAnimal):
- def __init__(self):
- print("子类:CDog 初始化")
-
-
- class CCat(CAnimal):
- def __init__(self):
- print("子类:CCat 初始化")
-
-
- if __name__ == "__main__":
- ani = CAnimal()
- jerry_mouse = CMouse()
- tom_cat = CCat()
-
- tom_cat.catch()
显示结果:首先查找当前类CCat,然后查找基类CAnimal,最后查找object类。
(<class '__main__.CCat'>, <class '__main__.CAnimal'>, <class 'object'>)
上面的查找结果中最后查找的是object类。
object类是python为所有对象提供的基类,提供有一些内置的属性和方法(可以使用dir()查看提供的内置属性和方法)。
所以建议在创建类时,如果没有父类,使用统一继承自object.
- class 类名(object):
- pass
通常情况下,子类拥有父类的所有方法和属性,可以直接使用父类中已经封装好的方法,不需要再次开发。但是当父类的方法实现不能满足子类需求时,可以对方法进行重写。
重写有两种情况:
当父类的方法实现和子类的方法实现完全不同,可以使用覆盖的方式,在子类中重新编写父类的方法的实现。覆盖的具体实现方式就是在子类中重新实现一个和父类同名的方法,在重写之后,运行时,子类只会调用子类中重写的方法,不会再调用父类封装的方法。
覆盖示例:
- class CAnimal:
- def __init__(self):
- print("父类:Animal 初始化")
-
- def eat(self):
- print("eating...")
-
- class CMouse(CAnimal):
- def __init__(self):
- print("子类:CDog 初始化")
-
- def eat(self):
- print("咯吱咯吱的吃")
-
- class CCat(CAnimal):
- def __init__(self):
- print("子类:CCat 初始化")
-
- def eat(self):
- print("优雅的吃")
-
- if __name__ == "__main__":
- ani = CAnimal()
- jerry_mouse = CMouse()
- tom_cat = CCat()
- ani.eat()
- jerry_mouse.eat()
- tom_cat.eat()
显示结果:
- 父类:Animal 初始化
- 子类:CDog 初始化
- 子类:CCat 初始化
- eating...
- 咯吱咯吱的吃
- 优雅的吃
如果子类的方法实现中包含父类的方法实现,或者说父类原本封装的方法实现是子类方法的一部分,此时,就可以使用扩展的方式,
super是python的一个特殊类;
super()标识使用super类创建一个对象;
常用的使用场景是在重写父类方法时,调用父类中封装的方法实现;
使用示例:
- class CAnimal:
- def __init__(self):
- print("父类:Animal 初始化")
-
- def eat(self):
- print("eating...")
-
- class CMouse(CAnimal):
- def __init__(self):
- print("子类:CDog 初始化")
-
- def eat(self):
- super().eat()
- print("咯吱咯吱的吃")
-
- class CCat(CAnimal):
- def __init__(self):
- print("子类:CCat 初始化")
-
- def eat(self):
- super().eat()
- print("优雅的吃")
-
- if __name__ == "__main__":
- ani = CAnimal()
- jerry_mouse = CMouse()
- tom_cat = CCat()
- ani.eat()
- jerry_mouse.eat()
- tom_cat.eat()
显示结果:
- 父类:Animal 初始化
- 子类:CDog 初始化
- 子类:CCat 初始化
- eating...
- eating...
- 咯吱咯吱的吃
- eating...
- 优雅的吃
多态增加了代码的灵活性,不同的子类对象调用相同的父类方法,产生不同的执行结果。多态以继承和重写父类方法为前提,不会影响到类的内部设计。

如上,CDog是CHuskie和CXiaoTianQuan的基类,在子类中重写了父类的play()方法,那么不同的子类对象调用play()方法时,产生的结果也不同。
示例:
- class CDog(object):
- def __init__(self,name):
- self.name = name
-
- def play(self):
- print(f"{self.name} 自己在玩耍")
-
- class CXiaoTianQuan(CDog):
- def play(self):
- print("%s 在追沉香" % self.name)
-
- class CHuskie(CDog):
- def play(self):
- print(" %s 在拆家" % self.name)
-
- class CPerson(object):
- def __init__(self,name):
- self.name = name
-
- def play_with(self,dog:CDog):
- print("%s想和%s一起玩耍,但是,"% (self.name,dog.name))
- dog.play()
-
- if __name__ == "__main__":
- xiaohei = CXiaoTianQuan("哮天犬")
- erha = CHuskie("哈士奇")
- lihua = CPerson('李华')
- lihua.play_with(xiaohei)
- lihua.play_with(erha)
显示结果,在CPerson类中的play_with()方法中不关注是什么类型的狗对象,直接调用play()方法;程序执行时,传入不同的实参,执行不同的结果。
- 李华想和哮天犬一起玩耍,但是,
- 哮天犬 在追沉香
- 李华想和哈士奇一起玩耍,但是,
- 哈士奇 在拆家
在python中,一切皆对象!!
在python中,类是一个特殊的对象——类对象。程序运行时,类会被加载到内存中,类对象在内存中只有一份,使用一个类可以创建出很多个对象实例。
类对象可以拥有自己的属性和方法:类属性、类方法。通过 类名. 的方式访问类属性或者调用类方法。该类的所有对象共享类属性和类方法。
类属性是给类对象定义的属性,通常用来记录与这个类相关的特性,不会记录具体对象的特征。使用赋值语句在class关键字下方可以定义类属性,如:
- class 类名(object):
- count=0 # count就是定义的类属性
如下示例,定义一个工具类,其定义如下:
| 类型 | 变量 | 说明 |
| 类属性 | count | 记录该类创建的对象的个数 |
| 实例属性 | name | 每一个对象都有一个name属性,展示实例对象本身 |
- class Tools(object):
- # 定义类属性count,记录创建的Tools实例对象的个数
- count = 0
- def __init__(self,name):
- # 定义对象属性
- self.name = name
-
- # 访问类属性,每创建一个对象count就加1
- Tools.count+=1
-
- if __name__ == "__main__":
- tool1 = Tools('钳子')
- tool2 = Tools('扳子')
- tool3 = Tools('剪子')
-
- print(f'tool1 name = {tool1.name}') # 输出 tool1 name = 钳子
- print(f'tool2 name = {tool2.name}') # 输出 tool2 name = 扳子
- print(f'tool3 name = {tool3.name}') # 输出 tool3 name = 剪子
- print(Tools.count) # 输出 3
类属性获取是向上查找机制,首先在对象内部查找对象属性,如果没有找到就会向上寻找类属性。
类方法是针对类对象定义的方法,在类方法内部可以直接访问类属性或者调用其它的类方法
类方法语法格式:
- @classmethod
- def 类方法名(cls):
- pass
说明:
示例,对如上Tools改造,增加类方法 show_tool_count()打印该类创建的对象的个数。
- class Tools(object):
-
- # 定义类属性count,记录创建的Tools实例对象的个数
- count = 0
-
- # 定义类方法
- @classmethod
- def show_tool_count(cls):
- print(f'Tools类创建的对象有{cls.count}个')
-
-
- def __init__(self,name):
- # 定义实例属性
- self.name = name
-
- # 访问类属性,每创建一个对象count就加1
- Tools.count+=1
-
- if __name__ == "__main__":
- tool1 = Tools('钳子')
- tool2 = Tools('扳子')
- tool3 = Tools('剪子')
-
- print(f'tool1 name = {tool1.name}') # 输出 tool1 name = 钳子
- print(f'tool2 name = {tool2.name}') # 输出 tool2 name = 扳子
- print(f'tool3 name = {tool3.name}') # 输出 tool3 name = 剪子
- print(Tools.count) # 输出 3
- Tools.show_tool_count() # 输出 Tools类创建的对象有3个
开发时,如果要封装一个类的方法,该方法不需要访问 实例属性、实例方法、类属性、类方法,那么就可以把这个方法封装成一个静态方法。
静态方法语法格式:
- @staticmethod
- def 静态方法名():
- pass
说明:
示例:
- class Tools(object):
- # 定义类属性count,记录创建的Tools实例对象的个数
- count = 0
-
- @classmethod
- def show_tool_count(cls):
- print(f'Tools类创建的对象有{cls.count}个')
-
- @staticmethod
- def run():
- print('开始工作了')
-
-
- def __init__(self,name):
- # 定义实例属性
- self.name = name
-
- # 访问类属性,每创建一个对象count就加1
- Tools.count+=1
-
- if __name__ == "__main__":
-
- Tools.run()
-
- tool1 = Tools('钳子')
- tool2 = Tools('扳子')
- tool3 = Tools('剪子')
-
- print(f'tool1 name = {tool1.name}') # 输出 tool1 name = 钳子
- print(f'tool2 name = {tool2.name}') # 输出 tool2 name = 扳子
- print(f'tool3 name = {tool3.name}') # 输出 tool3 name = 剪子
- print(Tools.count) # 输出 3
- Tools.show_tool_count() # 输出 Tools类创建的对象有3个
显示结果:
- 开始工作了
- tool1 name = 钳子
- tool2 name = 扳子
- tool3 name = 剪子
- 3
- Tools类创建的对象有3个
使用示例:
定义一个学生类Student,记录分数。类的属性和方法如下:

代码:
- class Student(object):
-
- # 定义类属性
- top_score = 0
- top_name = ''
-
- # 定义类方法
- @classmethod
- def show_top_score(cls):
- print(f'{cls.top_name}是第一名,分数是{cls.top_score}')
-
- # 定义静态方法
- @staticmethod
- def show_help():
- print('学生成绩管理')
-
- # 定义实例属性
- def __init__(self,name):
- self.name = name
-
- # 定义实例方法
- def record(self,score):
- if Student.top_score < score:
- Student.top_score = score
- Student.top_name = self.name
-
- if __name__ == '__main__':
- # 打印帮助信息
- Student.show_help()
-
- # 创建实例对象
- lihua = Student('李华')
- zhangsan = Student('张三')
- lisi = Student('李四')
-
- # 记录学生分数
- lihua.record(52.3)
- zhangsan.record(56.3)
- lisi.record(60)
-
- # 打印最高分和他的名字
- Student.show_top_score()
显示结果:
学生成绩管理
李四是第一名,分数是60
全文参考专栏面向对象部分。