Skip to content

类与对象详解

深入理解对象的创建与使用


类的结构

python
class ClassName:
    """类的文档字符串"""

    # 类属性(所有对象共享)
    class_variable = "shared"

    def __init__(self, param1, param2):
        """构造函数"""
        self.param1 = param1  # 实例属性
        self.param2 = param2

    def instance_method(self):
        """实例方法"""
        return self.param1

    @classmethod
    def class_method(cls):
        """类方法"""
        return cls.class_variable

    @staticmethod
    def static_method():
        """静态方法"""
        return "不依赖类或实例"

实例属性 vs 类属性

python
class Survey:
    # 类属性(所有调查共享)
    total_surveys = 0

    def __init__(self, name, year):
        # 实例属性(每个调查独有)
        self.name = name
        self.year = year
        Survey.total_surveys += 1  # 修改类属性

survey1 = Survey("收入调查", 2024)
survey2 = Survey("健康调查", 2024)

print(survey1.name)           # 收入调查(实例属性)
print(Survey.total_surveys)   # 2(类属性)

常用特殊方法

__init__:构造函数

python
class Respondent:
    def __init__(self, id, age, income):
        self.id = id
        self.age = age
        self.income = income
        print(f"创建受访者 {id}")

resp = Respondent(1001, 30, 75000)  # 自动调用 __init__

__str__:字符串表示

python
class Respondent:
    def __init__(self, id, age):
        self.id = id
        self.age = age

    def __str__(self):
        return f"Respondent(ID={self.id}, Age={self.age})"

resp = Respondent(1001, 30)
print(resp)  # Respondent(ID=1001, Age=30)

__repr__:开发者表示

python
class Respondent:
    def __init__(self, id, age):
        self.id = id
        self.age = age

    def __repr__(self):
        return f"Respondent({self.id}, {self.age})"

resp = Respondent(1001, 30)
print(repr(resp))  # Respondent(1001, 30)

__len__:长度

python
class Survey:
    def __init__(self, name):
        self.name = name
        self.responses = []

    def add_response(self, response):
        self.responses.append(response)

    def __len__(self):
        return len(self.responses)

survey = Survey("测试")
survey.add_response({'id': 1})
survey.add_response({'id': 2})
print(len(survey))  # 2

实战案例

案例:学生管理系统

python
class Student:
    """学生类"""
    school_name = "北京大学"  # 类属性

    def __init__(self, student_id, name, major, gpa=0.0):
        self.student_id = student_id
        self.name = name
        self.major = major
        self.gpa = gpa
        self.courses = []

    def enroll_course(self, course_name, credits):
        """选课"""
        self.courses.append({
            'name': course_name,
            'credits': credits
        })

    def get_total_credits(self):
        """计算总学分"""
        return sum(c['credits'] for c in self.courses)

    def update_gpa(self, new_gpa):
        """更新 GPA"""
        if 0 <= new_gpa <= 4.0:
            self.gpa = new_gpa
        else:
            print("GPA 必须在 0-4.0 之间")

    def __str__(self):
        return f"{self.name} ({self.major}, GPA: {self.gpa})"

# 使用
alice = Student(2024001, "Alice Wang", "Economics", 3.8)
alice.enroll_course("Microeconomics", 4)
alice.enroll_course("Econometrics", 4)

print(alice)
print(f"总学分: {alice.get_total_credits()}")
print(f"学校: {Student.school_name}")

案例:问卷数据容器

python
class SurveyData:
    """问卷数据管理类"""

    def __init__(self, survey_name):
        self.survey_name = survey_name
        self.responses = []

    def add_response(self, response):
        """添加响应"""
        if self._validate(response):
            self.responses.append(response)
            return True
        return False

    def _validate(self, response):
        """私有方法:验证数据"""
        required_fields = ['id', 'age', 'income']
        return all(field in response for field in required_fields)

    def get_average_income(self):
        """计算平均收入"""
        if not self.responses:
            return 0
        incomes = [r['income'] for r in self.responses]
        return sum(incomes) / len(incomes)

    def filter_by_age(self, min_age, max_age):
        """按年龄筛选"""
        return [r for r in self.responses
                if min_age <= r['age'] <= max_age]

    def __len__(self):
        return len(self.responses)

    def __str__(self):
        return f"{self.survey_name}: {len(self)} responses"

# 使用
survey = SurveyData("2024收入调查")
survey.add_response({'id': 1, 'age': 30, 'income': 75000})
survey.add_response({'id': 2, 'age': 35, 'income': 85000})

print(survey)
print(f"平均收入: ${survey.get_average_income():,.0f}")
print(f"30-40岁: {len(survey.filter_by_age(30, 40))} 人")

属性装饰器

@property:计算属性

python
class Respondent:
    def __init__(self, income, tax_rate=0.25):
        self.income = income
        self.tax_rate = tax_rate

    @property
    def net_income(self):
        """作为属性访问,而不是方法"""
        return self.income * (1 - self.tax_rate)

    @property
    def tax_amount(self):
        return self.income * self.tax_rate

resp = Respondent(100000, 0.3)
print(resp.net_income)   # 70000(像属性一样访问)
print(resp.tax_amount)   # 30000
# 不需要 resp.net_income()

封装:公有 vs 私有

python
class BankAccount:
    def __init__(self, balance):
        self.balance = balance       # 公有属性
        self._transactions = []      # 约定私有(单下划线)
        self.__pin = "1234"          # 真正私有(双下划线)

    def deposit(self, amount):
        """公有方法"""
        self.balance += amount
        self._log_transaction("deposit", amount)

    def _log_transaction(self, type, amount):
        """私有方法(约定)"""
        self._transactions.append({
            'type': type,
            'amount': amount
        })

account = BankAccount(1000)
account.deposit(500)
print(account.balance)         # 1500(可访问)
print(account._transactions)   # 可访问但不推荐
# print(account.__pin)         # AttributeError

练习题

练习 1:创建完整的类

python
# 创建 Course 类
# 属性: course_code, name, instructor, max_students, enrolled
# 方法:
#   - add_student(student_name): 添加学生(检查人数限制)
#   - drop_student(student_name): 移除学生
#   - is_full(): 是否已满
#   - get_enrollment_rate(): 返回选课率

练习 2:实现特殊方法

python
# 为 Survey 类实现:
# - __str__: 返回 "Survey: {name} ({count} responses)"
# - __len__: 返回响应数量
# - __getitem__: 支持 survey[0] 访问第 i 个响应

下一步

在下一节中,我们将了解 OOP 在数据科学中的应用。

继续!

基于 MIT 许可证发布。内容版权归作者所有。