二、面向对象编程
面向对象编程(Object-Oriented Programming,简称OOP)是一种通过组织对象来设计程序的编程方法。
Python天生就是面向对象的模块化编程。
1. 初识类和对象
示意图:
/-------> BYD E6(京A.88888) 实例,对象 车(类) \-------> BMW X5(京B.00000) 实例(对象)/-------> 小京巴(户籍号:000001) 狗(类) \-------> 导盲犬(户籍号:000002) /-------> 100 (对象) int(类) \-------> 200 (对象)
1.1 类→Class
物以类聚
1.1.1 概念
类是对一类对象的抽象,是对象的模板或蓝图。它定义了对象的属性(特征)和方法(功能)。
1.1.2 创建
数据成员:表明事物的特征。 相当于变量
方法成员:表明事物的功能。 相当于函数
通过
class
关键字定义类。类的创建语句语法:
class 类名 (继承列表):实例属性(类内的变量) 定义实例方法(类内的函数method) 定义类变量(class variable) 定义类方法(@classmethod) 定义静态方法(@staticmethod) 定义
参考代码:
class Dog:def __init__(self, name, age):self.name = nameself.age = age def bark(self):print(f"{self.name} says Woof!")
类名就是标识符,建议(有点强烈)首字母大写
类名实质上就是变量,它绑定一个类
self代表类实例化的对象本身
1.2 对象→Object
人以群分,每个人就是一个对象。
1.2.1 概念
对象是类的实例化,是类的实际数据存储,具有类所定义的属性和方法
1.2.2 创建
构造函数调用表达式
变量 = 类名([参数])
说明
变量存储的是实例化后的对象地址
类参数按照初始化方法的形参传递
对象是类的实例,具有类定义的属性和方法。
通过调用类的构造函数来创建对象。
每个对象有自己的状态,但共享方法。
示例代码
class Dog:pass # 创建第一个实例: dog1 = Dog() print(id(dog1)) # 打印这个对象的ID # 创建第二个实例对象 dog2 = Dog() # dog2 绑定一个Dog类型的对象 print(id(dog2)) class Person:def __init__(self, name, age):self.name = nameself.age = age def introduce(self):print(f"My name is {self.name} and I am {self.age} years old.")person1 = Person("Alice", 25) person2 = Person("Bob", 30)
类和对象的关系:
类是对象的模板或蓝图,定义了对象的属性和行为。
对象是类的实例,是根据类创建的具体实体。
同一个类可以创建多个对象,每个对象都有自己的状态。例如,一个Dog类可以创建多个 Dog 对象,每个对象有不同的 name 和 age。
2. 属性和方法
类的属性和方法是类的核心组成部分,它们用于定义类的状态和行为。
2.1 实例属性
每个实例有自己的变量,称为实例变量(也叫属性)
属性的使用语法
实例.属性名
属性使用
class Dog:def eat(self, food):print(self.color, '的', self.kinds, '正在吃', food) # 创建一个实例: dog1 = Dog() dog1.kinds = "京巴" # 添加属性 dog1.color = "白色" dog1.color = "黄色" # 修改属性 print(dog1.color, '的', dog1.kinds) dog2 = Dog() dog2.kinds = "藏獒" dog2.color = "棕色" print(dog2.color, '的', dog2.kinds)
实例方法和实例属性(实例变量)结合在一起用
class Dog:def eat(self, food):print(self.color, '的', self.kinds, '正在吃', food) # 创建第一个对象 dog1 = Dog() dog1.kinds = '京巴' # 添加属性kinds dog1.color = '白色' # 添加属性color dog1.eat("骨头") dog2 = Dog() dog2.kinds = '牧羊犬' dog2.color = '灰色' dog2.eat('包子')
2.2 实例方法
class 类名(继承列表):def 实例方法名(self, 参数1, 参数2, ...):"文档字符串"语句块
实例方法就是函数,至少有一个指向实例对象的形参self
调用
实例.实例方法名(调用传参) # 或 类名.实例方法名(实例, 调用传参)
带有实例方法的简单的Dog类
class Dog:"""这是一个种小动物的定义这种动物是狗(犬)类,用于创建各种各样的小狗""" def eat(self, food):"""此方法用来描述小狗吃东西的行为"""print("小狗正在吃", food) def sleep(self, hour):print("小狗睡了", hour, "小时!") def play(self, obj):print("小狗正在玩", obj) dog1 = Dog() dog1.eat("骨头") dog1.sleep(1) dog1.play('球') help(Dog) # 可以看到Dog类的文档信息
2.3 类属性
类属性是类的属性,此属性属于类,不属于此类的实例
作用:
通常用来存储该类创建的对象的共有属性
类属性说明
类属性,可以通过该类直接访问
类属性,可以通过类的实例直接访问
类属性示例
class Human:total_count = 0 # 创建类属性 self.name = namedef __init__(self, name):self.name = name print(Human.total_count) h1 = Human("小张") print(h1.total_count)
2.4 类方法
类方法是用于描述类的行为的方法,类方法属于类,不属于该类创建的对象
说明
类方法需要使用@classmethod装饰器定义
类方法至少有一个形参用于绑定类,约定为 $cls$
类和该类的实例都可以调用类方法
类方法不能访问此类创建的对象的实例属性
类方法示例1
class A:v = 0@classmethoddef set_v(cls, value): # def fun01(self)cls.v = value@classmethoddef get_v(cls):return cls.v print(A.get_v()) A.set_v(100) print(A.get_v()) a = A() print(a.get_v())
类方法示例2
class MyClass:class_attr = 0 # 类属性 def __init__(self, value):self.instance_attr = value # 实例属性 @classmethoddef modify_class_attr(cls, new_value):cls.class_attr = new_valueprint(f"类属性已修改为: {cls.class_attr}") @classmethoddef try_modify_instance_attr(cls):try:cls.instance_attr = 10 # 尝试修改实例属性(会失败)except AttributeError as e:print(f"错误: {e}") def show_attrs(self):print(f"实例属性: {self.instance_attr}")print(f"类属性: {self.class_attr}") # 创建类的实例 obj = MyClass(5) # 调用类方法修改类属性 MyClass.modify_class_attr(20) # 输出: 类属性已修改为: 20 obj.show_attrs() # 输出: # 实例属性: 5 # 类属性: 20 # 调用类方法尝试修改实例属性 MyClass.try_modify_instance_attr() # 输出: 错误: type object 'MyClass' has no attribute 'instance_attr' # 尝试调用类方法修改实例属性 obj.try_modify_instance_attr() # 输出: 错误: type object 'MyClass' has no attribute 'instance_attr'
cls说明
在Python中,cls
是一个约定俗成的名称,用于表示类本身。在类方法(使用 @classmethod
装饰的方法)中,cls
作为第一个参数传递给方法。这使得类方法可以访问和修改类属性以及调用其他类方法,而不需要引用具体的实例。
cls
的作用
访问类属性:类方法可以通过
cls
访问和修改类属性。调用类方法:类方法可以通过
cls
调用其他类方法。创建类实例:类方法可以使用
cls
来创建类的实例。
2.5 静态方法
静态方法定义在类的内部,作用域是类内部
说明
使用@staticmethod装饰器定义
不需要self和cls参数
通过类或类实例调用
可以访问类属性,不能访问实例属性
静态方法示例
class A:class_attr = 42 # 类属性 def __init__(self, value):self.instance_attr = value # 实例属性 @staticmethoddef myadd(a, b):# 只能访问传递的参数,不能访问实例属性return a + b # 创建类实例 a = A(10) # 调用静态方法 print(A.myadd(100, 200)) # 输出: 300 print(a.myadd(300, 400)) # 输出: 700 # 尝试在静态方法内访问类属性或实例属性(会导致错误) class B:class_attr = 42 def __init__(self, value):self.instance_attr = value @staticmethoddef myadd(a, b):# return a + b + B.class_attr# 以下访问会导致错误# return a + b + self.instance_attrreturn a + b # 创建类实例 b = B(10) # 调用静态方法 print(B.myadd(100, 200)) # 输出: 300 print(b.myadd(300, 400)) # 输出: 700
2.6 构造方法
构造方法__new__()
:
负责对象的 创建 和内存分配的。
在对象实例化时被调用,负责返回一个新的对象实例。
通常不需要显式地定义
__new__()
方法,Python会调用基类 $object$ 的__new__()
方法。
class MyClass:def __new__(cls, *args, **kwargs):print("调用 __new__ 方法,创建对象")return super().__new__(cls) def __init__(self, value):print("调用 __init__ 方法,初始化对象")self.value = value # 创建对象 obj = MyClass(10)
2.7 初始化方法
一旦对象被创建,Python会调用
__init__()
来初始化对象的属性。语法格式:
class 类名(继承列表):def __init__(self[, 形参列表]):语句块 # [] 代表其中的内容可省略
接收实参到
__init__
方法中代码示例:
class MyClass:def __new__(cls, *args, **kwargs):print("调用 __new__ 方法,创建对象")return super().__new__(cls) def __init__(self, value):print("调用 __init__ 方法,初始化对象")self.value = value # 创建对象 obj = MyClass(10)
输出:
调用 __new__ 方法,创建对象 调用 __init__ 方法,初始化对象
2.8 魔术方法
魔术方法是一种特殊的方法,用双下划线包裹,例如__init__
,__str__
,__add__
等。这些方法允许您自定义类的行为,以便与内置Python功能(如+运算符、迭代、字符串表示等)交互。
2.8.1 常用方法
__init__(self, ...)
: 初始化对象,通常用于设置对象的属性。__str__(self)
: 定义对象的字符串表示形式,可通过str(object)
或print(object)
调用。例如,您可以返回一个字符串,描述对象的属性。__repr__(self)
: 定义对象的“官方”字符串表示形式,通常用于调试。可通过repr(object)
调用。__len__(self)
: 定义对象的长度,可通过len(object)
调用。通常在自定义容器类中使用。__getitem__(self, key)
: 定义对象的索引操作,使对象可被像列表或字典一样索引。例如,object[key]
。__setitem__(self, key, value)
: 定义对象的赋值操作,使对象可像列表或字典一样赋值。例如,object[key] = value
。__delitem__(self, key)
: 定义对象的删除操作,使对象可像列表或字典一样删除元素。例如,del object[key]
。__iter__(self)
: 定义迭代器,使对象可迭代,可用于for
循环。__next__(self)
: 定义迭代器的下一个元素,通常与__iter__
一起使用。__add__(self, other)
: 定义对象相加的行为,使对象可以使用+
运算符相加。例如,object1 + object2
。__sub__(self, other)
: 定义对象相减的行为,使对象可以使用-
运算符相减。__eq__(self, other)
: 定义对象相等性的行为,使对象可以使用==
运算符比较。__lt__(self, other)
: 定义对象小于其他对象的行为,使对象可以使用<
运算符比较。__gt__(self, other)
: 定义对象大于其他对象的行为,使对象可以使用>
运算符比较。__call__(self, other)
是一个特殊的方法(也称为“魔法方法”),它允许一个对象像函数一样被调用。
2.8.2 案例参考
__str__()
:定义print()
或str()
函数调用时的输出字符串表示。__str__()
方法用来定义当你调用print()
或str()
时对象应该返回的字符串。该方法必须返回一个字符串,通常用于给用户可读的对象描述。
示例:
class Person:def __init__(self, name, age):self.name = nameself.age = age def __str__(self):return f"Person: {self.name}, Age: {self.age}" p = Person("Alice", 30) print(p) # 输出: Person: Alice, Age: 30
作用:
使得你的对象在打印时更加友好和可读。
提供更好的调试输出。
__repr__()
:定义repr()
函数的输出,通常用于开发和调试,给出对象的正式字符串表示。__repr__()
方法用于返回一个可以用来重新创建对象的字符串,理想情况下,返回的字符串应当是合法的 Python 表达式。如果你没有定义
__str__()
,那么__repr__()
会被用来替代str()
和print()
。
示例:
class Person:def __init__(self, name, age):self.name = nameself.age = age def __repr__(self):return f"Person('{self.name}', {self.age})" p = Person("Alice", 30) print(repr(p)) # 输出: Person('Alice', 30)
作用:
__repr__()
是供开发人员使用的,输出的字符串应该尽可能接近于可以创建该对象的代码。在调试时使用
repr()
更加方便,它提供了一个清晰的对象表示。
__add__()
:定义 $+$ 运算符的行为。该方法允许你重载加法操作符
+
,使其在两个对象之间执行加法时能够自定义行为。
示例:
class Point:def __init__(self, x, y):self.x = xself.y = y def __add__(self, other):return Point(self.x + other.x, self.y + other.y) def __repr__(self):return f"Point({self.x}, {self.y})" p1 = Point(1, 2) p2 = Point(3, 4) p3 = p1 + p2 print(p3) # 输出: Point(4, 6)
作用:
允许你自定义 $+$ 运算符的行为。
__len__()
:定义len()
函数的行为。__len__()
方法允许你自定义len()
函数的行为,返回对象的长度。
示例:
class MyList:def __init__(self, items):self.items = items def __len__(self):return len(self.items) my_list = MyList([1, 2, 3]) print(len(my_list)) # 输出: 3
作用:
使得你的对象能够与
len()
函数兼容,返回对象的长度。
__getitem__()
:定义对象支持索引([]
)操作。__getitem__()
方法使得对象能够支持类似列表、元组那样的索引操作。
示例:
class MyList:def __init__(self, items):self.items = items def __getitem__(self, index):return self.items[index] my_list = MyList([1, 2, 3, 4]) print(my_list[2]) # 输出: 3
作用:
使对象能够支持索引操作,类似于内置的数据结构(如列表、字典)。
__setitem__()
:定义对象支持索引赋值操作([] =
)。__setitem__()
方法允许你定义如何通过索引给对象赋值。
示例:
class MyList:def __init__(self, items):self.items = items def __setitem__(self, index, value):self.items[index] = value my_list = MyList([1, 2, 3]) my_list[1] = 10 print(my_list.items) # 输出: [1, 10, 3]
作用:
使对象能够支持索引赋值操作。
__del__()
:定义对象的析构方法,通常用于资源清理。这个方法在对象销毁时调用,通常用于清理资源(如关闭文件、数据库连接等)。
示例:
class MyClass:def __init__(self, name):self.name = nameprint(f"对象 {name} 创建") def __del__(self):print(f"对象 {self.name} 被销毁") obj = MyClass("Test") del obj # 输出: 对象 Test 被销毁
作用:
用于对象销毁时进行资源清理。