Python 类与对象
Python 面向对象编程 (OOP)
Section titled “Python 面向对象编程 (OOP)”Python 本质上是一种面向对象的语言。创建和使用类和对象是有效进行 Python 编程的核心部分。本节介绍 Python OOP 支持的关键概念。
如果您是面向对象编程(OOP)的新手,理解类、对象、继承、封装和多态等概念至关重要。请考虑回顾一下 OOP 的入门资料。
OOP 核心术语
Section titled “OOP 核心术语”- 类 (Class): 创建对象的蓝图或模板。定义了对象将拥有的属性 (数据) 和方法 (函数)。
- 对象 (实例) (Object / Instance): 类的具体实现。如果
Dog是一个类,那么像my_dog = Dog()这样的具体狗就是一个Dog类的对象或实例。 - 属性 (Attribute): 与类或对象关联的变量。
* 类变量 (Class Variable): 由类的所有实例共享。定义在类中,但在任何方法之外。
* 实例变量 (Instance Variable): 属于特定的实例/对象。通常在
__init__方法中使用self.variable_name定义。 - 方法 (Method): 在类中定义的函数。对该类的对象 (实例) 进行操作。实例方法的第一个参数通常命名为
self,代表实例本身。 __init__方法 (初始化器/构造器) (__init__Method / Initializer / Constructor): 当创建类的新对象 (实例) 时自动调用的特殊方法。用于初始化对象的属性。self: 实例方法的第一个参数的约定名称,表示调用该方法的实例。- 继承 (Inheritance): 一种机制,新类 (子类) 从现有类 (父类/超类) 继承属性和方法。促进代码重用。
- 封装 (Encapsulation): 将数据 (属性) 和操作数据的函数打包到一个单一单元 (类) 中。通常涉及控制对内部细节的访问 (例如,使用私有属性)。
- 多态 (Polymorphism): 不同类的对象对同一方法调用以特定于其类类型的方式响应的能力 (‘多种形式’)。
使用 class 关键字,后跟类名(通常使用驼峰命名法),以及一个冒号。缩进块包含类定义。
class ClassName: """可选的类描述文档字符串。""" # 类体: 属性和方法 pass # 对于空类体使用 'pass'示例: 一个简单的 Dog 类
Section titled “示例: 一个简单的 Dog 类”class Dog: """一个简单的狗的表示。"""
# 类变量 (由所有 Dog 实例共享) species = "Canis familiaris"
def __init__(self, name, age): """初始化狗的名字和年龄。""" # 实例变量 (每个实例唯一) self.name = name self.age = age print(f"名为 {self.name} 的狗已创建!")
# 实例方法 def bark(self): """模拟狗叫。""" print(f"{self.name} 说: 汪汪!")
# 另一个实例方法 def describe(self): """描述狗。""" print(f"{self.name} 今年 {self.age} 岁了。")要点:
创建实例对象
Section titled “创建实例对象”要创建一个类的对象(实例),就像调用函数一样调用类名,传递 __init__ 方法所需的任何参数(不包括 self)。
# 创建两个 Dog 对象my_dog = Dog("Buddy", 3)your_dog = Dog("Lucy", 5)__init__ 方法的输出:
Dog named Buddy created!Dog named Lucy created!访问属性和调用方法
Section titled “访问属性和调用方法”使用点运算符(.)访问对象的属性(实例变量)并调用其方法。
# 访问实例变量print(f"{my_dog.name} 今年 {my_dog.age} 岁了。")
# 调用实例方法my_dog.bark()your_dog.describe()
# 访问类变量 (可以通过实例或类访问)print(f"Buddy 属于的物种: {my_dog.species}")print(f"所有狗都属于: {Dog.species}")输出:
Buddy is 3 years old.Buddy says: Woof!Lucy is 5 years old.Buddy belongs to the species: Canis familiarisAll dogs belong to: Canis familiaris您可以直接修改实例属性(如果它们不打算设为私有)。您还可以动态地向特定实例添加新属性。
my_dog.age = 4 # 修改现有属性my_dog.breed = "Golden Retriever" # 仅向 my_dog 添加一个新属性
print(f"Buddy 更新后的年龄: {my_dog.age}")print(f"Buddy 的品种: {my_dog.breed}")
# your_dog 最初没有 'breed' 属性# print(your_dog.breed) # 会引发 AttributeError
# 修改类变量会影响所有实例Dog.species = "A good doggo"print(f"Lucy 现在属于的物种: {your_dog.species}")内置属性函数:
getattr(obj, name[, default]): 访问obj的属性name。如果指定default且属性不存在则返回default,否则引发AttributeError。hasattr(obj, name): 如果obj有属性name则返回True,否则返回False。setattr(obj, name, value): 将obj上的属性name设置为value。如果属性不存在则创建。delattr(obj, name): 从obj中删除属性name。
Python 类有几个内置属性,可以通过点运算符访问:
ClassName.__doc__: 类的文档字符串(或None)。ClassName.__name__: 类名。ClassName.__module__: 定义类的模块名称。ClassName.__bases__: 包含父类(基类)的元组。ClassName.__dict__: 包含类命名空间的字典(类变量、方法等)。instance.__class__: 实例所属的类。
print(f"Dog.__doc__: {Dog.__doc__}")print(f"Dog.__name__: {Dog.__name__}")print(f"Dog.__module__: {Dog.__module__}")print(f"Dog.__bases__: {Dog.__bases__}") # 如果没有显式继承则为空元组# print(f"Dog.__dict__: {Dog.__dict__}") # 显示类属性/方法print(f"my_dog.__class__: {my_dog.__class__}")垃圾回收和析构器 (__del__)
Section titled “垃圾回收和析构器 (__del__)”Python 主要基于引用计数自动管理内存进行垃圾回收。当一个对象的引用计数降为零(没有变量指向它)时,Python 会回收其内存。
您可以定义一个特殊方法 __del__(self),称为析构器,Python 会在对象被垃圾回收之前尝试调用它。它用于清理任务,如关闭与对象关联的文件或网络连接。
注意: 由于垃圾回收的时机和循环引用可能导致的问题,依赖 __del__ 可能会很棘手。对于资源管理,通常最好使用上下文管理器(with 语句)。
class FileHandler: def __init__(self, filename): print(f"Opening {filename}") self.filename = filename # self.file = open(filename, 'w') # 示例资源
def __del__(self): # 清理操作 print(f"Destructor called for {self.filename}. Cleaning up.") # if hasattr(self, 'file') and not self.file.closed: # self.file.close()
fh = FileHandler("my_data.txt")# 一段时间后,当 'fh' 不再被引用时...del fh # 手动触发引用计数下降# ... 垃圾回收最终会调用 __del__继承允许一个类(子类)从另一个类(父类/超类)继承属性和方法。语法: class ChildClass(ParentClass):
class Animal: def __init__(self, name): self.name = name print("Animal 已创建")
def speak(self): print("通用的动物声音")
def eat(self): print(f"{self.name} 正在吃东西。")
# Cat 继承自 Animalclass Cat(Animal): def __init__(self, name, breed): print("Cat 构造器被调用") # 调用父类的 __init__ 方法 super().__init__(name) self.breed = breed
# 重写 speak 方法 def speak(self): print(f"{self.name} 说: 喵喵!")
# 添加一个 Cat 特有的新方法 def purr(self): print(f"{self.name} 正在发出呼噜声。")
# 创建子类的一个实例my_cat = Cat("Whiskers", "Siamese")
my_cat.eat() # 从 Animal 继承的方法my_cat.speak() # 在 Cat 中重写的方法my_cat.purr() # Cat 特有的方法print(f"Breed: {my_cat.breed}")输出:
Cat constructor calledAnimal createdWhiskers is eating.Whiskers says: Meow!Whiskers is purring.Breed: Siamesesuper().__init__(...) 用于调用父类的初始化器。
多重继承: 一个类可以从多个父类继承: class Child(Parent1, Parent2):。Python 使用方法解析顺序(Method Resolution Order, MRO)来决定在冲突时调用哪个父类方法(C3 线性化算法)。
子类可以提供从父类继承的方法的自己的实现。这称为重写(Overriding)。见 Cat 示例中的 speak 方法。
特殊(魔法/双下划线)方法
Section titled “特殊(魔法/双下划线)方法”Python 类可以定义带有双下划线(__methodname__)的特殊方法,这些方法允许对象与 Python 内置操作(如加法、打印、比较)进行交互。
| 方法 | 运算符/函数 | 描述 |
|---|---|---|
__init__(self, ...) | 对象创建 | 初始化器 |
__str__(self) | str(), print() | 非正式的、人类可读的字符串表示 |
__repr__(self) | repr() | 正式的、无歧义的字符串表示(通常是用于重新创建对象的代码) |
__len__(self) | len() | 对象的长度 |
__add__(self, other) | + | 加法 |
__eq__(self, other) | == | 相等比较 |
__lt__(self, other) | < | 小于比较 |
__getitem__(self, key) | obj[key] | 按键访问项(索引/查找) |
__call__(self, ...) | obj(...) | 使对象可以像函数一样调用 |
示例 (运算符重载 + 和 __str__):
Section titled “示例 (运算符重载 + 和 __str__):”class Vector: def __init__(self, x, y): self.x = x self.y = y
def __add__(self, other): """使用 + 运算符定义向量加法。""" if isinstance(other, Vector): return Vector(self.x + other.x, self.y + other.y) return NotImplemented # 指示操作不支持
def __str__(self): """定义向量如何打印。""" return f"Vector({self.x}, {self.y})"
def __repr__(self): """定义 '官方' 表示形式。""" return f"Vector(x={self.x}, y={self.y})"
v1 = Vector(2, 3)v2 = Vector(5, -1)v3 = v1 + v2 # 使用 __add__ 方法
print(v1) # 使用 __str__print(v3) # 使用 __str__print(repr(v3)) # 使用 __repr__输出:
Vector(2, 3)Vector(7, 2)Vector(x=7, y=2)数据隐藏 (名称修饰)
Section titled “数据隐藏 (名称修饰)”Python 没有像某些语言那样严格的“私有”属性。但是,通过在属性名前添加两个下划线(例如,__secret),Python 会执行名称修饰(name mangling)。它会在内部将属性名更改为 _ClassName__attributeName。这使得从类外部意外访问变得更加困难(但并非不可能),并防止子类中的命名冲突。
class MySecret: def __init__(self): self.public_var = 10 self.__secret_var = 20 # 名称被修饰
def get_secret(self): return self.__secret_var
secret_obj = MySecret()print(secret_obj.public_var) # 访问 public 属性没问题
# print(secret_obj.__secret_var) # AttributeError!
# 通过修饰后的名称访问 (不推荐,除非必要)print(secret_obj._MySecret__secret_var)
print(secret_obj.get_secret()) # 首选通过方法访问输出:
102020按照惯例,单个下划线前缀(_variable)表示属性或方法仅供内部使用,但这并不受 Python 强制执行。
属性 (Properties) 和数据类 (Data Classes)
Section titled “属性 (Properties) 和数据类 (Data Classes)”现代 Python 提供了 @property 等特性,用于创建受管属性(将 getter/setter/deleter 方法伪装成简单的属性访问)和 @dataclass(来自 dataclasses 模块),用于在创建主要用于存储数据的类时减少样板代码。