第十章:类和对象
10.1 什么是类?
类就像一个制造小盒子的模具,你可以用这个模具制造出很多相似的小盒子(对象)。每个小盒子都有自己的特性(属性)和行为(方法),它们都遵循同一个设计蓝图。
我们可以理解为把数据和函数包在一起。
# 定义一个简单的类classStudent:"""学生类"""# 数据def__init__(self,name,score):self.name=name# 属性:姓名self.score=score# 属性:成绩# 方法(函数)defprint_info(self):"""方法:打印学生信息"""print(f"学生姓名:{self.name},成绩:{self.score}")10.2 类和对象的基本概念
10.2.1 类与对象的关系
- 类:是抽象的蓝图,定义了一组对象共有的属性和方法
- 对象:是类的实例,具有类所定义的属性和方法
# 使用类创建对象student1=Student("张三",90)student2=Student("李四",85)# 每个对象都有自己的属性print(student1.name)# 张三print(student2.name)# 李四# 调用对象的方法student1.print_info()# 学生姓名:张三,成绩:90student2.print_info()# 学生姓名:李四,成绩:8510.2.2 类的组成
一个类通常包含:
- 属性:对象的特征或数据
- 方法:对象的行为或功能
- 构造函数:初始化对象的方法
10.3 定义类
10.3.1 基本语法
使用class关键字定义类:
classClassName:"""类的文档字符串"""def__init__(self,参数1,参数2,...):# 初始化方法,用于设置对象的初始状态self.属性1=参数1self.属性2=参数2# ...def方法1(self,参数...):# 方法定义pass10.3.2 初始化方法__init__
__init__是一个特殊的方法,在创建对象时自动调用,用于初始化对象的属性:
classDog:"""狗类"""def__init__(self,name,age):self.name=name self.age=age self.breed="未知"# 设置默认值# 创建对象时必须提供__init__中必需的参数my_dog=Dog("旺财",3)self参数是对当前对象的引用,在类的方法中必须作为第一个参数:
- 通过
self可以访问对象的属性和其他方法 - 在调用方法时,不需要手动传递
self,Python会自动处理
10.4 属性和方法
10.4.1 实例属性
实例属性属于每个对象实例,每个对象都有自己的属性副本:
classCar:"""汽车类"""def__init__(self,brand,color):self.brand=brand# 实例属性self.color=color# 实例属性car1=Car("丰田","白色")car2=Car("本田","黑色")print(car1.brand)# 丰田print(car2.brand)# 本田10.4.2 实例方法
实例方法是在类中定义的函数,第一个参数是self,用于定义对象的行为:
classCar:def__init__(self,brand,color):self.brand=brand self.color=color self.speed=0# 初始速度defaccelerate(self,increment):"""加速方法"""self.speed+=incrementprint(f"{self.brand}车加速到{self.speed}km/h")defbrake(self,decrement):"""刹车方法"""self.speed-=decrementifself.speed<0:self.speed=0print(f"{self.brand}车减速到{self.speed}km/h")# 使用实例方法my_car=Car("宝马","蓝色")my_car.accelerate(20)# 宝马车加速到20km/hmy_car.brake(5)# 宝马车减速到15km/h10.4.3 类属性
类属性属于类本身,所有对象实例共享同一个类属性:
classCar:# 类属性wheels=4# 所有汽车都有4个轮子def__init__(self,brand,color):self.brand=brand# 实例属性self.color=color# 实例属性# 通过类名访问类属性print(Car.wheels)# 4# 通过对象实例也可以访问类属性my_car=Car("奥迪","红色")print(my_car.wheels)# 4# 修改类属性会影响所有实例Car.wheels=6print(my_car.wheels)# 610.4.4 类方法
类方法使用@classmethod装饰器定义,第一个参数是cls(表示类本身):
classCar:wheels=4count=0# 记录创建的汽车数量def__init__(self,brand,color):self.brand=brand self.color=color Car.count+=1# 每次创建对象时计数增加@classmethoddefget_count(cls):"""类方法:获取汽车数量"""returncls.count@classmethoddefset_wheels(cls,number):"""类方法:设置轮子数量"""cls.wheels=number# 使用类方法print(Car.get_count())# 0(还没有创建对象)car1=Car("奔驰","黑色")car2=Car("大众","白色")print(Car.get_count())# 2Car.set_wheels(6)print(car1.wheels)# 610.4.5 静态方法
静态方法使用@staticmethod装饰器定义,不需要self或cls参数:
classCar:def__init__(self,brand,color):self.brand=brand self.color=color@staticmethoddefis_valid_brand(brand):"""静态方法:检查品牌是否有效"""valid_brands=["丰田","本田","宝马","奔驰","奥迪"]returnbrandinvalid_brands# 使用静态方法print(Car.is_valid_brand("宝马"))# Trueprint(Car.is_valid_brand("比亚迪"))# False# 也可以通过对象调用my_car=Car("宝马","蓝色")print(my_car.is_valid_brand("丰田"))# True10.5 封装
封装是将数据(属性)和行为(方法)包装在一起,并控制对内部数据的访问。
10.5.1 访问控制
在Python中,使用命名约定来控制访问:
- 公有属性和方法:正常命名,如
name、print_info() - 受保护的属性和方法:以单个下划线开头,如
_protected_var - 私有的属性和方法:以双下划线开头,如
__private_var
classBankAccount:"""银行账户类"""def__init__(self,account_holder,balance):self.account_holder=account_holder# 公有属性self._account_number="123456789"# 受保护属性(约定上不直接访问)self.__balance=balance# 私有属性(无法直接访问)# 公有方法defget_balance(self):"""获取余额(公有方法)"""returnself.__balancedefdeposit(self,amount):"""存款"""ifamount>0:self.__balance+=amountreturnTruereturnFalsedefwithdraw(self,amount):"""取款"""if0<amount<=self.__balance:self.__balance-=amountreturnTruereturnFalse# 使用封装account=BankAccount("张三",1000)print(account.account_holder)# 张三(可以访问公有属性)# print(account.__balance) # 错误!无法直接访问私有属性print(account.get_balance())# 1000(通过公有方法访问私有属性)account.deposit(500)print(account.get_balance())# 1500account.withdraw(200)print(account.get_balance())# 130010.5.2 属性装饰器
使用@property装饰器可以创建只读属性,或者定义getter、setter方法:
classCircle:"""圆类"""def__init__(self,radius):self._radius=radius@propertydefradius(self):"""radius属性的getter方法"""returnself._radius@radius.setterdefradius(self,value):"""radius属性的setter方法"""ifvalue>=0:self._radius=valueelse:raiseValueError("半径不能为负")@propertydefarea(self):"""计算面积(只读属性)"""return3.14159*self._radius**2@propertydefcircumference(self):"""计算周长(只读属性)"""return2*3.14159*self._radius# 使用属性装饰器circle=Circle(5)print(circle.radius)# 5(调用getter方法)circle.radius=10# 调用setter方法print(circle.radius)# 10print(circle.area)# 314.159(只读属性)print(circle.circumference)# 62.8318(只读属性)# circle.radius = -5 # 会抛出ValueError10.6 继承
继承允许我们定义一个类,它继承另一个类的属性和方法。
10.6.1 基本继承
# 父类(基类)classAnimal:"""动物类"""def__init__(self,name,age):self.name=name self.age=agedefeat(self):print(f"{self.name}正在吃东西")defsleep(self):print(f"{self.name}正在睡觉")# 子类(派生类)classDog(Animal):"""狗类,继承自动物类"""def__init__(self,name,age,breed):# 调用父类的初始化方法super().__init__(name,age)self.breed=breed# 子类特有的属性# 子类特有的方法defbark(self):print(f"{self.name}在汪汪叫")# 重写父类的方法defeat(self):print(f"{self.name}正在吃狗粮")# 使用继承my_dog=Dog("旺财",3,"金毛")# 调用继承的方法my_dog.eat()# 旺财正在吃狗粮(重写后的方法)my_dog.sleep()# 旺财正在睡觉(继承的方法)# 调用子类特有的方法my_dog.bark()# 旺财在汪汪叫print(my_dog.breed)# 金毛(子类特有的属性)10.6.2 多重继承
一个类可以继承多个父类:
classFlyable:"""可飞行的类"""deffly(self):print("正在飞行")classSwimmable:"""可游泳的类"""defswim(self):print("正在游泳")classDuck(Flyable,Swimmable):"""鸭子类,继承自Flyable和Swimmable"""def__init__(self,name):self.name=namedefquack(self):print(f"{self.name}在嘎嘎叫")# 使用多重继承duck=Duck("唐老鸭")duck.quack()# 唐老鸭在嘎嘎叫duck.fly()# 正在飞行duck.swim()# 正在游泳10.6.3 方法解析顺序
当使用多重继承时,Python按照方法解析顺序来查找方法:
classA:deftest(self):print("A")classB(A):deftest(self):print("B")classC(A):deftest(self):print("C")classD(B,C):pass# 查看MROprint(D.__mro__)d=D()d.test()# 输出B,按照MRO顺序查找10.7 多态
多态是指不同类的对象对同一消息(方法调用)做出不同的响应:
classAnimal:defspeak(self):passclassDog(Animal):defspeak(self):return"汪汪!"classCat(Animal):defspeak(self):return"喵喵!"classCow(Animal):defspeak(self):return"哞哞!"# 多态的例子defanimal_speak(animal):print(animal.speak())# 传入不同的对象,调用相同的方法,得到不同的结果dog=Dog()cat=Cat()cow=Cow()animal_speak(dog)# 汪汪!animal_speak(cat)# 喵喵!animal_speak(cow)# 哞哞!10.8 特殊方法(魔术方法)
特殊方法以双下划线开头和结尾,用于实现类的特定行为:
10.8.1 常见特殊方法
classBook:"""图书类"""def__init__(self,title,author,pages):self.title=title self.author=author self.pages=pages# 字符串表示def__str__(self):returnf"《{self.title}》 by{self.author}"def__repr__(self):returnf"Book('{self.title}', '{self.author}',{self.pages})"# 长度def__len__(self):returnself.pages# 比较def__eq__(self,other):ifisinstance(other,Book):returnself.title==other.titleandself.author==other.authorreturnFalsedef__lt__(self,other):"""小于比较,按页数比较"""ifisinstance(other,Book):returnself.pages<other.pagesreturnNotImplemented# 使用特殊方法book1=Book("Python编程","John",300)book2=Book("算法导论","Tom",500)print(book1)# 《Python编程》 by John(调用__str__)print(repr(book1))# Book('Python编程', 'John', 300)(调用__repr__)print(len(book1))# 300(调用__len__)print(book1==book2)# False(调用__eq__)print(book1<book2)# True(调用__lt__)10.8.2 算术运算特殊方法
classVector:"""向量类"""def__init__(self,x,y):self.x=x self.y=ydef__add__(self,other):"""向量加法"""ifisinstance(other,Vector):returnVector(self.x+other.x,self.y+other.y)returnNotImplementeddef__sub__(self,other):"""向量减法"""ifisinstance(other,Vector):returnVector(self.x-other.x,self.y-other.y)returnNotImplementeddef__mul__(self,scalar):"""向量数乘"""ifisinstance(scalar,(int,float)):returnVector(self.x*scalar,self.y*scalar)returnNotImplementeddef__str__(self):returnf"Vector({self.x},{self.y})"# 使用算术运算特殊方法v1=Vector(2,3)v2=Vector(1,4)v3=v1+v2# 向量加法print(v3)# Vector(3, 7)v4=v1-v2# 向量减法print(v4)# Vector(1, -1)v5=v1*3# 向量数乘print(v5)# Vector(6, 9)本章笔记:
- 类是创建对象的蓝图,定义了对象的属性和方法。
- 使用
class关键字定义类,使用__init__方法初始化对象。 - 类包含实例属性、实例方法、类属性和类方法。
- 封装通过访问控制保护对象内部数据。
- 继承允许子类继承父类的属性和方法,并可以重写或扩展。
- 多态允许不同类的对象对同一方法调用做出不同响应。
- 特殊方法(魔术方法)用于实现类的特定行为。
- 面向对象编程使代码更加模块化、可重用和易于维护。