Python 类和对象的基本概念及属性和方法的常见分类和使用场景
一、引言
1、面向对象编程(OOP)的重要性
面向对象编程(Object-Oriented Programming)是现代软件开发的核心范式之一,它通过将数据和操作数据的方法绑定在一起,形成"对象"的概念,极大地提高了代码的可重用性、可维护性和可扩展性。OOP的三大特性——封装、继承和多态,为解决复杂软件工程问题提供了系统化的方法。
# 终极类比:类=汽车设计图,对象=实际生产的汽车
class CarDesign: # 类
def __init__(self, brand):
self.brand = brand # 属性
def run(self): # 方法
print(f"{self.brand}加速中...")
my_car = CarDesign("Tesla") # 实例化对象
my_car.run() # 调用方法 → 输出:Tesla加速中...
2、Python中类和对象的地位
在Python中,一切皆对象。类和对象是Python语言的基础构建块:
- 类(Class)是创建对象的蓝图,定义了对象的属性和方法
- 对象(Object)是类的实例,包含具体的数据和行为
- Python通过简洁的语法实现了完整的面向对象特性
3、属性分类矩阵(附内存分布解析)
| 属性类型 | 声明位置 | 生命周期 | 典型场景 |
|---|---|---|---|
| 实例属性 | __init__内 |
随对象存在 | 对象独有数据(如:员工工资) |
| 类属性 | 类内部直接声明 | 程序运行全程 | 共享配置(如:税率常量) |
| 私有属性 | __前缀 |
同实例属性 | 内部状态封装 |
二、Python类和对象的基本概念
1. 什么是类(Class)
类的定义:类是创建对象的蓝图或模板,它封装了一组相关的数据和操作这些数据的方法。类定义了对象将拥有的属性和行为。
类的声明语法:
class ClassName:
"""类的文档字符串"""
# 类属性(所有实例共享)
class_attribute = value
def __init__(self, parameters):
"""构造方法,初始化实例属性"""
self.instance_attribute = parameters
def instance_method(self):
"""实例方法"""
# 方法体
2. 什么是对象(Object)
对象是类的实例:对象是根据类定义创建的具体实体,包含类中定义的数据和方法的具体实现。
创建对象的语法:
# 创建类的实例(对象)
object_name = ClassName(arguments)
示例:
class Dog:
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
def bark(self):
print(f"{self.name} says: Woof!")
# 创建Dog类的对象
my_dog = Dog("Buddy", 3)
your_dog = Dog("Max", 5)
3. 类与对象的关系
类比:类就像模具,对象就像用模具生产出的产品。一个模具可以生产多个相似但独立的产品。
一个类可以创建多个对象:
# 使用同一个类创建多个不同的对象
dog1 = Dog("Charlie", 2)
dog2 = Dog("Bella", 4)
dog3 = Dog("Luna", 1)
# 每个对象都有独立的属性
print(dog1.name) # 输出: Charlie
print(dog2.name) # 输出: Bella
print(dog3.name) # 输出: Luna
# 调用对象的方法
dog1.bark() # 输出: Charlie says: Woof!
dog2.bark() # 输出: Bella says: Woof!
关键点:
- 类定义了对象的结构和行为
- 对象是类的具体实例
- 每个对象都有自己独立的状态(属性值)
- 多个对象可以共享相同的方法定义
三、类的属性及其分类
1. 实例属性
定义:实例属性是属于特定对象的属性,每个对象都有自己独立的实例属性副本。
初始化方式:通常在类的 __init__ 方法中进行初始化。
class Student:
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age # 实例属性
# 创建对象
student1 = Student("Alice", 20)
student2 = Student("Bob", 22)
print(student1.name) # Alice
print(student2.name) # Bob (不同的对象,属性值不同)
2. 类属性
定义:类属性是属于类本身的属性,所有实例共享同一个类属性。
使用场景:适合存储类级别的数据,如常量、计数器等。
class Circle:
# 类属性
pi = 3.14159
def __init__(self, radius):
self.radius = radius # 实例属性
def area(self):
return self.pi * self.radius ** 2
# 访问类属性
print(Circle.pi) # 3.14159
# 所有实例共享相同的类属性
circle1 = Circle(5)
circle2 = Circle(10)
print(circle1.pi) # 3.14159
print(circle2.pi) # 3.14159
# 修改类属性会影响所有实例
Circle.pi = 3.14
print(circle1.area()) # 使用新的pi值计算
3. 私有属性
命名约定:以双下划线 __ 开头(但不以双下划线结尾)的属性被视为私有属性。
访问限制:只能在类内部访问,外部直接访问会触发 AttributeError。
class BankAccount:
def __init__(self, account_number, balance):
self.account_number = account_number # 公开属性
self.__balance = balance # 私有属性
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def get_balance(self):
return self.__balance
account = BankAccount("123456", 1000)
# 访问公开属性
print(account.account_number) # 123456
# 直接访问私有属性会报错
# print(account.__balance) # AttributeError
# 通过类提供的方法访问私有属性
print(account.get_balance()) # 1000
account.deposit(500)
print(account.get_balance()) # 1500
注意事项:
- Python的私有属性实际上是通过名称修饰(name mangling)实现的,并非完全不可访问
- 可以使用
_ClassName__attribute的方式强制访问(但不推荐) - 单下划线
_开头的属性是约定俗成的"保护"属性,表示"不要随意访问",但Python并不强制限制
四、类的方法及其分类
1. 实例方法
定义:实例方法是操作实例属性的方法,是最常用的方法类型。
特点:
- 第一个参数必须是
self,表示当前对象实例 - 可以访问和修改实例属性
- 可以调用其他实例方法、类方法和静态方法
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
# 实例方法
def display_info(self):
print(f"《{self.title}》 by {self.author}")
book = Book("Python编程", "Guido van Rossum")
book.display_info() # 《Python编程》 by Guido van Rossum
2. 类方法
定义:类方法是操作类属性的方法,与整个类相关而非特定实例。
特点:
- 使用
@classmethod装饰器 - 第一个参数必须是
cls,表示当前类 - 可以访问和修改类属性
- 常用于创建替代构造函数
class Pizza:
# 类属性
sizes = ["small", "medium", "large"]
def __init__(self, ingredients, size="medium"):
self.ingredients = ingredients
self.size = size
@classmethod
def margherita(cls, size="medium"):
# 使用cls创建新实例
return cls(["tomato", "mozzarella", "basil"], size)
@classmethod
def add_size(cls, new_size):
# 修改类属性
cls.sizes.append(new_size)
# 使用类方法创建对象
p1 = Pizza.margherita()
print(p1.ingredients) # ['tomato', 'mozzarella', 'basil']
# 修改类属性
Pizza.add_size("extra-large")
print(Pizza.sizes) # ['small', 'medium', 'large', 'extra-large']
3. 静态方法
定义:静态方法与类和实例都无关,只是逻辑上属于类的实用方法。
特点:
- 使用
@staticmethod装饰器 - 不需要
self或cls参数 - 不能访问类或实例属性
- 常用于工具函数
class MathOperations:
@staticmethod
def add(x, y):
return x + y
@staticmethod
def factorial(n):
if n == 0:
return 1
return n * MathOperations.factorial(n-1)
# 调用静态方法
print(MathOperations.add(5, 3)) # 8
print(MathOperations.factorial(5)) # 120
4. 特殊方法(魔术方法)
定义:以双下划线开头和结尾的方法,用于实现类的特殊行为。
常见示例:
__init__: 构造函数
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 25)
__str__: 字符串表示
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Person(name={self.name}, age={self.age})"
p = Person("Bob", 30)
print(p) # Person(name=Bob, age=30)
__len__: 长度定义
class Playlist:
def __init__(self, songs):
self.songs = songs
def __len__(self):
return len(self.songs)
pl = Playlist(["Song1", "Song2", "Song3"])
print(len(pl)) # 3
其他常用魔术方法
class Vector:
"""
Vector类代表一个二维向量。
它提供了向量的初始化、加法、相等性比较和字符串表示功能。
"""
def __init__(self, x, y):
"""
初始化向量的x和y分量。
参数:
x -- 向量的x分量
y -- 向量的y分量
"""
self.x = x
self.y = y
# 加法运算符重载
def __add__(self, other):
"""
实现向量加法。
参数:
other -- 另一个Vector实例
返回:
一个新的Vector实例,表示两个向量的和
"""
return Vector(self.x + other.x, self.y + other.y)
# 相等判断
def __eq__(self, other):
"""
判断两个向量是否相等。
参数:
other -- 另一个Vector实例
返回:
如果两个向量相等返回True,否则返回False
"""
return self.x == other.x and self.y == other.y
# 格式化输出
def __repr__(self):
"""
提供向量的字符串表示。
返回:
向量的字符串表示形式
"""
return f"Vector({self.x}, {self.y})"
# 创建两个向量实例
v1 = Vector(2, 3)
v2 = Vector(4, 5)
# 测试向量加法和相等性比较
print(v1 + v2) # Vector(6, 8)
print(v1 == v2) # False
该代码定义了一个[Vector]类,表示二维向量。
- [__init__]:初始化向量的x和y分量。
- [__add__]:重载加法运算,实现两个向量相加并返回新实例。
- [__eq__]:判断两个向量是否在x和y分量上都相等。
- [__repr__]:返回向量的字符串表示,便于输出和调试。
总结:
- 实例方法:操作实例,需要
self - 类方法:操作类,需要
cls,使用@classmethod - 静态方法:独立工具函数,使用
@staticmethod - 魔术方法:实现特殊行为,如
__init__,__str__等
五、属性和方法的常见使用场景
1. 实例属性和方法的使用场景
适用情况:当数据和行为是对象特有的,每个对象都需要维护自己的状态时。
典型示例:用户管理系统中的用户对象
class User:
def __init__(self, username, email):
"""
构造函数:初始化用户对象
:param username: 用户名
:param email: 用户邮箱
"""
# 实例属性:每个用户独有的数据
self.username = username
self.email = email
self.login_attempts = 0
def increment_login_attempts(self):
"""
增加登录尝试次数
"""
# 登录尝试次数增加
self.login_attempts += 1
def reset_login_attempts(self):
"""
重置登录尝试次数
"""
# 登录尝试次数重置为0
self.login_attempts = 0
def display_user_info(self):
"""
显示用户信息
"""
# 打印用户信息
print(f"Username: {self.username}")
print(f"Email: {self.email}")
print(f"Login attempts: {self.login_attempts}")
# 创建用户实例
user1 = User("alice123", "alice@example.com")
user2 = User("bob456", "bob@example.com")
# 操作用户特定状态
user1.increment_login_attempts()
user1.increment_login_attempts()
user2.increment_login_attempts()
# 显示用户信息
user1.display_user_info()
# Username: alice123
# Email: alice@example.com
# Login attempts: 2
user2.display_user_info()
# Username: bob456
# Email: bob@example.com
# Login attempts: 1
该代码定义了一个[User]类,用于表示用户信息及操作用户登录尝试次数。功能如下:
1. [__init__]:初始化用户名、邮箱,并设置登录尝试次数为0;
2. [increment_login_attempts]:每次调用增加一次登录尝试次数;
3. [reset_login_attempts]:将登录尝试次数重置为0;
4. [display_user_info]:打印用户信息及当前登录尝试次数。
2. 类属性和方法的使用场景
适用情况:当数据或行为需要被所有实例共享,或者需要在不创建实例的情况下操作类时。
典型示例:计数器、配置管理
class Car:
# 类属性:所有汽车共享的数据
total_cars = 0
default_color = "red"
def __init__(self, model):
"""
构造方法:初始化汽车实例
参数:
model - 汽车型号
"""
self.model = model
# 修改类属性
Car.total_cars += 1
@classmethod
def get_total_cars(cls):
"""
类方法:获取汽车总数
返回值:
汽车总数
"""
return cls.total_cars
@classmethod
def set_default_color(cls, color):
"""
类方法:设置汽车默认颜色
参数:
color - 新的默认颜色
"""
cls.default_color = color
# 不创建实例即可访问类属性
print(Car.default_color) # red
# 创建汽车实例
car1 = Car("Model S")
car2 = Car("Model 3")
# 访问类属性
print(Car.get_total_cars()) # 2
# 修改类属性影响所有实例
Car.set_default_color("blue")
print(Car.default_color) # blue
该代码定义了一个[Car]类,具有实例初始化、类方法获取和修改类属性的功能。具体如下:
1. [__init__]构造方法,初始化汽车型号,并增加汽车总数;
2. [get_total_cars]类方法,返回创建的汽车总数;
3. [set_default_color]类方法,设置所有汽车的默认颜色。
最后通过类名调用类方法和属性,展示了类属性在所有实例间的共享特性。
3. 静态方法的使用场景
适用情况:当函数逻辑上与类相关,但不依赖于类或实例状态时。
典型示例:数学计算、格式转换
class StringUtils:
"""
字符串工具类,提供静态方法来处理字符串
"""
@staticmethod
def is_palindrome(text: str) -> bool:
"""
检查给定的字符串是否是回文
参数:
text (str): 要检查的字符串
返回:
bool: 如果字符串是回文则返回True,否则返回False
"""
# 移除字符串中的空格并转换为小写,以进行回文检查
text = text.lower().replace(" ", "")
# 检查处理后的字符串是否与其反转相等
return text == text[::-1]
@staticmethod
def to_camel_case(text: str) -> str:
"""
将下划线命名法的字符串转换为驼峰命名法
参数:
text (str): 下划线命名法的字符串
返回:
str: 驼峰命名法的字符串
"""
# 将字符串按'_下划线'分割为部分
parts = text.split("_")
# 将第一部分与其余部分的首字母大写形式连接起来
return parts[0] + "".join(word.capitalize() for word in parts[1:])
# 使用静态方法
print(StringUtils.is_palindrome("Madam")) # True
print(StringUtils.to_camel_case("first_name")) # firstName
该类包含两个静态方法:
1. [is_palindrome(text)]判断字符串是否为回文。
将字符串统一为小写并去除空格,再与反转后的字符串比较。
2. [to_camel_case(text)]将下划线命名转为驼峰命名。
以“_”分割字符串,首部分保留,后续部分首字母大写后拼接。
4. 私有属性和方法的使用场景
适用情况:需要隐藏内部实现细节,保护数据不被外部直接访问或修改时。
典型示例:银行账户余额管理
class BankAccount:
"""
银行账户类,用于模拟银行账户的基本功能。
属性:
- account_id: 账户ID,用于唯一标识一个银行账户。
- __balance: 账户余额,私有属性,防止外部直接访问。
方法:
- get_balance: 获取当前账户余额。
- deposit: 存款操作,增加账户余额。
- withdraw: 取款操作,减少账户余额。
- __log_transaction: 记录交易日志,私有方法,用于内部记录交易详情。
"""
def __init__(self, account_id, initial_balance=0):
"""
构造器,初始化银行账户。
参数:
- account_id: 账户ID。
- initial_balance: 初始余额,默认为0。
"""
self.account_id = account_id
self.__balance = initial_balance # 私有属性,账户余额
# 公开方法,用于获取私有属性__balance的值
def get_balance(self):
"""
获取当前账户余额。
返回:
- 当前账户余额。
"""
return self.__balance
def deposit(self, amount):
"""
存款操作,增加账户余额。
参数:
- amount: 存款金额,必须大于0。
"""
if amount > 0:
self.__balance += amount
print(f"Deposited {amount}. New balance: {self.__balance}")
else:
print("Deposit amount must be positive")
def withdraw(self, amount):
"""
取款操作,减少账户余额。
参数:
- amount: 取款金额,必须大于0且不超过当前余额。
"""
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"Withdrew {amount}. New balance: {self.__balance}")
else:
print("Invalid withdrawal amount")
# 私有方法,用于内部实现细节,外部不应直接调用
def __log_transaction(self, transaction_type, amount):
"""
记录交易日志。
参数:
- transaction_type: 交易类型,如"deposit"表示存款。
- amount: 交易金额。
"""
print(f"Logged: {transaction_type} of {amount}")
# 实例化银行账户对象
account = BankAccount("123456", 1000)
# 合法操作,通过公开方法与账户交互
account.deposit(500) # Deposited 500. New balance: 1500
account.withdraw(200) # Withdrew 200. New balance: 1300
# 以下操作将引发错误,因为直接访问私有属性和方法是不被允许的
# print(account.__balance) # AttributeError
# account.__log_transaction("deposit", 100) # AttributeError
# 正确方式来访问账户余额
print(account.get_balance()) # 1300
该代码定义了一个银行账户类 [BankAccount]具备以下功能:
1. **私有属性 [__balance]用于存储账户余额,外部无法直接访问。
2. **构造函数 [__init__]初始化账户ID和初始余额。
3. **[get_balance]提供对私有属性 [__balance](file://D:\CDHQYJ\python\hqyjpy\day8\11.py#L0-L0) 的只读访问。
4. **[deposit]实现存款功能,金额需大于0。
5. **[withdraw]实现取款功能,金额需大于0且不超过余额。
6. **私有方法 [__log_transaction]:记录交易日志,仅类内部使用。
示例中展示了合法的存款和取款操作,并通过公开方法获取余额。外部无法直接访问私有属性或方法。
总结:
- 实例成员:处理对象特有状态和行为
- 类成员:管理共享数据和类级别操作
- 静态方法:提供与类相关的工具函数
- 私有成员:封装实现细节,保护关键数据
六、实际案例演示
案例1:电商系统中的商品类
class Product:
# 类属性
tax_rate = 0.1 # 10%税率
def __init__(self, name, price, quantity):
"""
构造方法,初始化Product对象。
参数:
name (str): 产品名称。
price (float): 产品单价。
quantity (int): 产品数量。
"""
# 实例属性
self.name = name
self.price = price
self.quantity = quantity
self.__discount = 0 # 私有属性,用于存储折扣百分比
def apply_discount(self, percent):
"""
应用折扣到产品价格。
参数:
percent (float): 折扣百分比,范围0到100。
"""
if 0 <= percent <= 100:
self.__discount = percent
@classmethod
def set_tax_rate(cls, new_rate):
"""
类方法,设置新的税率。
参数:
new_rate (float): 新的税率。
"""
cls.tax_rate = new_rate
@staticmethod
def format_price(price):
"""
静态方法,格式化价格为货币字符串。
参数:
price (float): 需要格式化的价格。
返回:
str: 格式化后的价格字符串。
"""
return f"${price:.2f}"
def __str__(self):
"""
特殊方法,返回产品的字符串表示。
返回:
str: 产品的名称和价格信息。
"""
return f"{self.name} - {self.format_price(self.price)}"
这段代码定义了一个 [Product]类,用于表示商品,具备初始化属性、折扣应用、税率设置、价格格式化和字符串表示等功能。具体功能如下:
1. [__init__]初始化产品名称、价格、数量,并设置默认折扣为0(私有属性)。
2. [apply_discount]设置合法范围(0~100)内的折扣百分比。
3. [set_tax_rate]类方法):修改所有产品共享的税率(类属性)。
4. [format_price](静态方法):将价格格式化为美元字符串(如 `$10.00`)。
5. [__str__]:返回产品的字符串表示,包含名称和格式化价格。
案例2:游戏开发中的角色类
class Character:
# 定义角色类,此类表示游戏角色的基本属性和行为
MAX_HEALTH = 100 # 角色的最大生命值,作为类属性存在
def __init__(self, name, attack_power):
# 初始化角色实例,设置角色的基本属性
# 参数:
# - name: 角色名称
# - attack_power: 角色的攻击力
self.name = name
self.health = self.MAX_HEALTH
self.attack_power = attack_power
self.__inventory = [] # 角色的私有属性,表示角色的物品库存
def attack(self, target):
# 角色执行攻击的行为
# 参数:
# - target: 攻击的目标角色实例
target.health -= self.attack_power
print(f"{self.name} attacks {target.name}!")
def heal(self, amount):
# 角色执行自我治疗的行为
# 参数:
# - amount: 治疗的量
self.health = min(self.health + amount, self.MAX_HEALTH)
def add_to_inventory(self, item):
# 向角色的物品库存中添加物品
# 参数:
# - item: 要添加的物品
self.__inventory.append(item)
def show_inventory(self):
# 显示角色的物品库存
# 返回: 格式化后的库存字符串,如果没有物品则返回"Empty"
return ", ".join(self.__inventory) if self.__inventory else "Empty"
该代码定义了一个`Character`类,表示角色,具有以下功能:
1. [__init__]:初始化角色名字、生命值(等于最大生命值100)、攻击力和私有背包。
2. `attack`:攻击目标,减少目标生命值,并输出攻击信息。
3. `heal`:恢复自身生命值,不超过最大值。
4. `add_to_inventory`:将物品添加到私有背包中。
5. [show_inventory]:返回背包中的物品列表,若为空则返回"Empty"。
七、最佳实践和常见陷阱
最佳实践
1. 遵循单一职责原则
# 不好的实践:一个类做太多事情
class UserManager:
def __init__(self, user):
self.user = user
def authenticate(self):
# 认证逻辑
def save_to_db(self):
# 数据库操作
def send_welcome_email(self):
# 发送邮件
# 好的实践:拆分职责
class Authenticator:
def authenticate(self, user):
# 认证逻辑
class UserRepository:
def save(self, user):
# 数据库操作
class EmailService:
def send_welcome(self, user):
# 发送邮件
2. 合理使用私有属性和方法
class TemperatureSensor:
def __init__(self):
self.__current_temp = 0 # 私有属性
def __read_hardware(self): # 私有方法
# 读取硬件传感器的复杂逻辑
return 25.5
def update(self):
self.__current_temp = self.__read_hardware()
def get_temp(self):
return self.__current_temp
3. 适当使用类方法和静态方法
class DateUtil:
@staticmethod
def is_valid_date(date_str):
# 验证日期格式
pass
@classmethod
def from_iso_format(cls, iso_str):
# 从ISO格式创建日期对象
return cls(iso_str)
4. 良好的命名规范
# 好的命名示例
class ShoppingCart:
def calculate_total(self):
pass
def apply_discount(self, coupon_code):
pass
# 避免使用模糊的名称
class MyClass: # 太泛泛
def func1(self): # 没有描述性
pass
常见陷阱
1. 过度使用类属性导致状态混乱
class GameCharacter:
health = 100 # 类属性
def __init__(self, name):
self.name = name
# 问题:所有实例共享同一个health值
hero1 = GameCharacter("Hero1")
hero2 = GameCharacter("Hero2")
GameCharacter.health = 80 # 影响所有实例
2. 混淆实例方法和静态方法
class Calculator:
def __init__(self):
self.result = 0
# 错误:应该用静态方法
def add(self, x, y):
return x + y
# 正确用法
@staticmethod
def static_add(x, y):
return x + y
# 实例方法的正确用法
def accumulate(self, x):
self.result += x
3. 忽略self参数
class Person:
def __init__(name): # 缺少self参数
self.name = name # 会报错
def greet(): # 缺少self
print(f"Hello, {self.name}") # 会报错
4. 滥用继承
# 不好的继承:不满足"is-a"关系
class Email(Document):
# Email不是一种Document,这是错误的继承
# 好的做法:使用组合
class Email:
def __init__(self):
self.attachments = [] # 包含Document对象
八、总结
-
类和对象是Python面向对象编程的核心:Python中一切皆对象,类是创建对象的蓝图
-
合理使用不同类别的属性和方法:
- 实例成员处理对象特有状态
- 类成员管理共享数据
- 静态方法提供工具函数
- 私有成员封装实现细节
-
根据实际需求选择合适的设计模式:
- 简单场景使用基本OOP特性
- 复杂场景考虑工厂模式、策略模式等
- 优先考虑组合而非继承
九、扩展阅读与练习
推荐阅读
- Python官方文档 - 类
- 《流畅的Python》第8章:对象引用、可变性和回收
- 《Python设计模式》第2章:创建型模式
这篇博文将从基础概念入手,逐步深入到实际应用,通过丰富的代码示例和实际案例,帮助读者全面理解Python中类和对象的使用。每个部分都配有清晰的解释和实用的代码片段,使读者能够边学边练,快速掌握相关知识。
更多推荐
所有评论(0)