10 类的使用
面向对象编程 (OOP) 是现代软件开发的主流范式之一,而 类 (Class) 则是 OOP 的核心概念。在 Python 中,类为我们提供了一种强大的方式来创建自定义数据类型、封装数据和行为、构建复杂的系统。这篇博客将带你深入探索 Python 类的方方面面。
一、什么是类?对象又是什么?
- 类 (Class): 可以将其视为一个蓝图或模板。它定义了某种事物(例如:
Dog、Car、BankAccount)应该具有哪些属性(如:名字、颜色、余额)和能够执行哪些操作(如:叫、加速、存款)。类本身只是一个定义,并不占用实际的内存空间来存储具体的数据。
- 对象 (Object) / 实例 (Instance): 当你根据类这个蓝图实际创建出一个具体的东西时,这个东西就是一个对象或实例。例如,根据
Dog 类,你可以创建出名叫 buddy 的狗对象,它有具体的属性值(buddy.name = "Buddy")并能执行具体的方法(buddy.bark())。对象是类的具体化,占用内存存储其属性值。
简单来说:类定义结构,对象是具体存在。
二、定义一个简单的类
在 Python 中,使用 class 关键字来定义类,后面跟着类名(通常采用 PascalCase 命名规范,即每个单词首字母大写)。
| class Dog:
pass # 最简单的类定义,目前什么也没做
|
现在,我们可以使用这个 Dog 类来创建对象:
| my_dog = Dog() # 创建 Dog 类的一个实例/对象,赋值给变量 my_dog
print(my_dog) # 输出类似 <__main__.Dog object at 0x000001F2D4C6B310>,表示这是一个 Dog 对象
|
三、初始化方法:__init__
我们创建了 Dog 对象,但它还没有任何属性(名字、品种等)。我们需要一种在创建对象时就设置初始属性的方法。这就是 __init__ 方法的作用。
__init__ 是一个特殊的构造方法。当你创建类的新实例时(例如 my_dog = Dog()),Python 会自动调用 __init__ 方法。
| class Dog:
def __init__(self, name, breed):
self.name = name # 将传入的 name 参数赋值给实例的 name 属性
self.breed = breed # 将传入的 breed 参数赋值给实例的 breed 属性
# 创建对象时传入初始值
buddy = Dog("Buddy", "Golden Retriever")
fido = Dog("Fido", "Labrador")
print(buddy.name) # 输出: Buddy
print(fido.breed) # 输出: Labrador
|
self 参数: 这是 __init__ 方法的第一个参数,它代表当前正在创建的实例对象本身。Python 自动传递这个参数,我们不需要显式传入它(例如 buddy = Dog("Buddy", "Golden Retriever"),没有给 self 传值)。
self.name 和 self.breed: 这行代码创建了实例属性。self.name = name 意思是“将传入的 name 参数的值,赋给这个特定实例 (self) 的 name 属性”。这样,每个 Dog 对象就有了自己独立的 name 和 breed。
四、实例方法
方法是定义在类内部的函数,用于描述对象可以执行的操作。方法的第一个参数也总是 self,指向调用该方法的实例对象。
| class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
def bark(self): # 定义 bark 方法
print(f"{self.name} says: Woof! Woof!") # 使用实例的属性
def describe(self):
print(f"I am a {self.breed} named {self.name}.")
# 使用
buddy = Dog("Buddy", "Golden Retriever")
buddy.bark() # 输出: Buddy says: Woof! Woof!
buddy.describe() # 输出: I am a Golden Retriever named Buddy.
|
方法 bark() 和 describe() 可以访问并操作该实例的属性(self.name, self.breed)。
五、类属性与实例属性
- 实例属性: 属于特定实例的属性,如上面
buddy.name。每个实例的属性值可以不同。通常在 __init__ 中初始化。
- 类属性: 属于类本身的属性,所有实例共享。定义在类内部,但在任何方法之外(通常紧跟在
class 语句下方)。
| class Dog:
species = "Canis familiaris" # 类属性,所有狗共享的物种
def __init__(self, name, breed):
self.name = name # 实例属性
self.breed = breed # 实例属性
# 访问类属性 (通过类名或实例)
print(Dog.species) # 输出: Canis familiaris
buddy = Dog("Buddy", "Golden Retriever")
print(buddy.species) # 输出: Canis familiaris (实例访问类属性)
# 修改类属性会影响所有实例
Dog.species = "Canis lupus familiaris"
print(buddy.species) # 输出: Canis lupus familiaris
|
注意:如果通过一个实例去设置一个与类属性同名的属性,Python 会为该实例创建一个新的实例属性,而不会修改类属性。这可能会造成混淆(所以设置实例的时候不要和类名冲突)。
六、继承 (Inheritance)
继承是 OOP 的重要特性,它允许我们创建一个新类(子类或派生类)来继承另一个类(父类或基类)的属性和方法。子类可以:
- 继承父类的所有功能。
- 扩展,添加自己新的属性和方法。
- 覆盖 (Override),重新定义父类已有的方法以实现不同的行为。
| class Animal: # 父类
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} makes a sound.")
class Dog(Animal): # 子类 Dog 继承自 Animal
def __init__(self, name, breed):
super().__init__(name) # 调用父类的 __init__ 初始化 name
self.breed = breed
def speak(self): # 覆盖父类的 speak 方法
print(f"{self.name} says: Woof! Woof!")
class Cat(Animal): # 子类 Cat 继承自 Animal
def speak(self): # 覆盖父类的 speak 方法
print(f"{self.name} says: Meow!")
# 使用
generic = Animal("Generic")
generic.speak() # 输出: Generic makes a sound.
buddy = Dog("Buddy", "Golden")
buddy.speak() # 输出: Buddy says: Woof! Woof!
whiskers = Cat("Whiskers")
whiskers.speak() # 输出: Whiskers says: Meow!
|
super(): 在子类中,super().__init__(name) 调用父类 Animal 的 __init__ 方法来初始化 name 属性,避免了代码重复。
- 方法覆盖:
Dog 和 Cat 都定义了自己的 speak 方法,覆盖了父类的实现,实现了多态(同一个方法名,不同类的对象调用时产生不同行为)。
七、封装 (Encapsulation)
封装是将数据(属性)和操作数据的方法(方法)捆绑在一起,并隐藏内部实现的细节。在 Python 中,主要通过命名约定来实现访问控制,而不是严格的访问限制(像 Java 的 private 关键字)。
- 公有 (Public): 属性和方法名直接公开(如
self.name, self.bark())。
- 保护 (Protected): 约定使用单个下划线开头(如
_internal_data)。这是一种提示,表示“请勿直接访问,除非你知道自己在做什么”,但 Python 并不阻止访问。
- 私有 (Private): 约定使用双下划线开头(如
__very_secret)。Python 会对这些名称进行名称重整 (Name Mangling),使其在类外部难以直接访问(实际名称会变成 _ClassName__very_secret)。主要用于避免子类意外覆盖内部属性。
| class BankAccount:
def __init__(self, account_holder, initial_balance=0):
self.account_holder = account_holder # 公有
self._account_number = "ACC123456" # 保护 (约定)
self.__balance = initial_balance # 私有 (名称重整)
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"Deposited ${amount}. New balance: ${self.__balance}")
else:
print("Invalid deposit amount.")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"Withdrew ${amount}. New balance: ${self.__balance}")
else:
print("Invalid withdrawal amount or insufficient funds.")
def get_balance(self): # 提供公有方法来访问私有属性(控制访问)
return self.__balance
# 使用
account = BankAccount("Alice", 100)
print(account.account_holder) # Alice (公有,可直接访问)
print(account._account_number) # ACC123456 (保护,虽然能访问,但不建议)
# print(account.__balance) # 直接访问会报错: AttributeError
print(account.get_balance()) # 100 (通过方法访问)
account.deposit(50) # Deposited $50. New balance: $150
account.withdraw(30) # Withdrew $30. New balance: $120
account.__balance = 1000 # 这样做不会修改真正的私有变量,只是创建了一个新的公有属性
print(account.get_balance()) # 还是 120
print(account.__balance) # 1000 (新创建的公有属性),和私有属性不一样,只是一种约定,不建议直接访问
account.deposit(500) # Deposited $500. New balance: $620
print(account.get_balance()) # 620,可以看到方法修改的还是私有属性
|
八、特殊方法
特殊方法是以双下划线开头和结尾的方法(如 __init__, __str__, __len__, __add__)。它们由 Python 解释器在特定场景下自动调用,让我们自定义类的行为以更自然地融入 Python 环境。
__str__(self): 定义对象的“非正式”字符串表示。当使用 print(obj) 或 str(obj) 时调用,通常返回易于人类阅读的信息。
__len__(self): 定义对象的“长度”。当使用 len(obj) 时调用。
__add__(self, other): 定义 + 操作符的行为(obj1 + obj2)。
__eq__(self, other): 定义 == 操作符的行为。
- ... 还有很多,如
__getitem__, __setitem__, __call__ 等。
| class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Vector({self.x}, {self.y})" # 用户友好的表示
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# 使用
v1 = Vector(2, 3)
v2 = Vector(4, 5)
v3 = v1 + v2 # 调用 v1.__add__(v2)
print(v3) # 输出: Vector(6, 8) (调用 __str__)
print(v1 == Vector(2, 3)) # 输出: True (调用 __eq__)
|
九、实际案例:一个简单的计算器类
| class Calculator:
def __init__(self):
self.memory = 0 # 存储计算结果
def add(self, a, b):
result = a + b
self.memory = result
return result
def subtract(self, a, b):
result = a - b
self.memory = result
return result
def multiply(self, a, b):
result = a * b
self.memory = result
return result
def divide(self, a, b):
if b == 0:
raise ValueError("Cannot divide by zero!")
result = a / b
self.memory = result
return result
def recall_memory(self):
return self.memory
def clear_memory(self):
self.memory = 0
# 使用
calc = Calculator()
print(calc.add(10, 5)) # 输出: 15
print(calc.multiply(3, 4)) # 输出: 12
print(calc.recall_memory()) # 输出: 12 (记住最后一次操作结果)
calc.clear_memory()
print(calc.recall_memory()) # 输出: 0
|
Python 的类是实现面向对象编程的强大工具。通过理解类、对象、__init__、实例方法、类属性、实例属性、继承、封装以及特殊方法,你可以构建出结构清晰、可复用、可维护的代码。类让你能够更好地模拟现实世界中的实体和关系,是编写复杂 Python 应用程序的基础。不断实践,尝试用类来解决你遇到的问题!