目录

Python 类与对象

学习目标

  • 理解面向对象编程(OOP)的基本概念
  • 掌握类的定义和对象的创建
  • 理解实例属性、类属性和方法的区别
  • 掌握 __init__ 构造方法和 self 的含义

https://img.zhaojq.top/20260730221649813.jpg
Python面向对象核心

一、什么是面向对象编程

面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,将数据和操作数据的方法组织在一起,形成"对象"。

核心概念:

  • 类(Class):对象的蓝图或模板
  • 对象(Object):类的实例
  • 属性(Attribute):对象的数据
  • 方法(Method):对象的行为

二、定义类

class Dog:
    """狗类"""

    # 类属性(所有实例共享)
    species = "Canis familiaris"

    # 构造方法(初始化实例)
    def __init__(self, name, age):
        # 实例属性(每个实例独立)
        self.name = name
        self.age = age

    # 实例方法
    def bark(self):
        return f"{self.name} says: 汪汪!"

    def info(self):
        return f"{self.name} 今年 {self.age} 岁"


# 创建对象(实例化)
dog1 = Dog("Buddy", 3)
dog2 = Dog("Luna", 2)

# 访问属性和方法
print(dog1.name)           # Buddy
print(dog1.age)            # 3
print(dog1.bark())         # Buddy says: 汪汪!
print(dog1.info())         # Buddy 今年 3 岁

print(dog2.name)           # Luna
print(dog2.bark())         # Luna says: 汪汪!

三、__init__ 构造方法

__init__ 是类的构造方法,在创建对象时自动调用,用于初始化实例属性。

class Person:
    def __init__(self, name, age=18):
        """
        构造方法
        name: 姓名(必需)
        age: 年龄(默认 18)
        """
        self.name = name
        self.age = age
        self._id = None          # 受保护属性(约定俗成)

    def set_id(self, id_number):
        self._id = id_number

    def get_id(self):
        return self._id


# 使用
p1 = Person("Alice", 25)
p2 = Person("Bob")           # 使用默认年龄

print(p1.name, p1.age)       # Alice 25
print(p2.name, p2.age)       # Bob 18

四、self 是什么

self 代表类的实例本身,它是实例方法的第一个参数(约定俗成的名称)。

class Counter:
    def __init__(self):
        self.count = 0

    def increment(self):
        self.count += 1
        return self.count

    def reset(self):
        self.count = 0


c = Counter()
print(c.increment())   # 1
print(c.increment())   # 2
print(c.increment())   # 3

c.reset()
print(c.count)         # 0

等价调用方式

# 以下两种调用等价
c.increment()          # 隐式传递 self
Counter.increment(c)   # 显式传递 self

五、类属性 vs 实例属性

class Student:
    # 类属性(所有实例共享)
    school = "北京大学"
    count = 0

    def __init__(self, name):
        # 实例属性(每个实例独立)
        self.name = name
        self.scores = []
        Student.count += 1   # 修改类属性

    def add_score(self, score):
        self.scores.append(score)


# 创建学生
s1 = Student("Alice")
s2 = Student("Bob")

# 访问类属性
print(s1.school)       # 北京大学
print(s2.school)       # 北京大学
print(Student.school)  # 北京大学

# 访问实例属性
print(s1.name)         # Alice
print(s2.name)         # Bob

# 类属性共享
print(Student.count)   # 2(两个实例)

# 修改类属性
Student.school = "清华大学"
print(s1.school)       # 清华大学
print(s2.school)       # 清华大学

# 通过实例修改类属性(危险!会创建实例属性)
s1.school = "复旦大学"  # 这里创建了一个实例属性,不影响类属性
print(s1.school)       # 复旦大学(实例属性)
print(s2.school)       # 清华大学(类属性)
print(Student.school)  # 清华大学

六、类方法、静态方法与实例方法

class MathUtils:
    # 类属性
    PI = 3.14159

    def __init__(self, name):
        self.name = name

    # 实例方法(需要 self)
    def greet(self):
        return f"Hello, {self.name}"

    # 类方法(需要 cls,可访问类属性)
    @classmethod
    def circle_area(cls, radius):
        return cls.PI * radius ** 2

    # 静态方法(不需要 self 或 cls,类似普通函数)
    @staticmethod
    def add(a, b):
        return a + b


# 调用实例方法
m = MathUtils("Alice")
print(m.greet())           # Hello, Alice

# 调用类方法
print(MathUtils.circle_area(5))   # 78.53975
print(m.circle_area(5))           # 也可以

# 调用静态方法
print(MathUtils.add(3, 5))        # 8
print(m.add(3, 5))                # 也可以
方法类型 装饰器 第一个参数 用途
实例方法 self 操作实例属性
类方法 @classmethod cls 操作类属性、替代构造方法
静态方法 @staticmethod 与类相关的工具函数

类方法作为替代构造方法

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def from_string(cls, date_str):
        """从字符串创建日期对象"""
        year, month, day = map(int, date_str.split("-"))
        return cls(year, month, day)

    @classmethod
    def today(cls):
        """创建今天的日期对象"""
        import datetime
        now = datetime.datetime.now()
        return cls(now.year, now.month, now.day)

    def __str__(self):
        return f"{self.year}-{self.month:02d}-{self.day:02d}"


# 使用替代构造方法
d1 = Date(2024, 1, 15)
d2 = Date.from_string("2024-06-01")
d3 = Date.today()

print(d1)   # 2024-01-15
print(d2)   # 2024-06-01
print(d3)   # 今天的日期

七、访问控制

Python 没有严格的访问控制,使用命名约定:

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self._balance = balance        # 受保护属性(单下划线)
        self.__password = "123456"     # 私有属性(双下划线)

    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
            return True
        return False

    def withdraw(self, amount):
        if 0 < amount <= self._balance:
            self._balance -= amount
            return True
        return False

    def get_balance(self):
        return self._balance

    def _internal_method(self):        # 受保护方法
        pass

    def __private_method(self):        # 私有方法
        pass


account = BankAccount("Alice", 1000)
print(account.owner)       # Alice
print(account._balance)    # 1000(可以访问,但不建议)
# print(account.__password)  # AttributeError!

# 双下划线属性被改名(名称修饰)
print(account._BankAccount__password)   # 123456(不要这样做)

account.deposit(500)
print(account.get_balance())   # 1500
account.withdraw(200)
print(account.get_balance())   # 1300

八、综合示例

"""
图书管理系统
"""

class Book:
    """图书类"""
    total_books = 0

    def __init__(self, title, author, isbn):
        self.title = title
        self.author = author
        self.isbn = isbn
        self._is_borrowed = False
        Book.total_books += 1

    def borrow(self):
        if not self._is_borrowed:
            self._is_borrowed = True
            return f"《{self.title}》借阅成功"
        return f"《{self.title}》已被借出"

    def return_book(self):
        if self._is_borrowed:
            self._is_borrowed = False
            return f"《{self.title}》归还成功"
        return f"《{self.title}》未被借出"

    def get_status(self):
        status = "已借出" if self._is_borrowed else "在馆"
        return f"《{self.title}》- {status}"

    def __str__(self):
        return f"《{self.title}》by {self.author} (ISBN: {self.isbn})"


class Library:
    """图书馆类"""

    def __init__(self, name):
        self.name = name
        self.books = {}

    def add_book(self, book):
        self.books[book.isbn] = book
        return f"已添加 {book.title}"

    def find_book(self, isbn):
        return self.books.get(isbn)

    def list_books(self):
        print(f"\n=== {self.name} 藏书列表 ===")
        for book in self.books.values():
            print(f"  {book} - {book.get_status()}")


# 使用
lib = Library("城市图书馆")

book1 = Book("Python编程", "张三", "978-1")
book2 = Book("数据结构", "李四", "978-2")
book3 = Book("算法导论", "王五", "978-3")

lib.add_book(book1)
lib.add_book(book2)
lib.add_book(book3)

lib.list_books()

print(f"\n{book1.borrow()}")
print(f"{book2.borrow()}")

lib.list_books()

print(f"\n{book1.return_book()}")
print(f"\n图书馆总藏书: {Book.total_books}")

小结

  • 类是对象的模板,使用 class 关键字定义
  • __init__ 是构造方法,用于初始化实例属性
  • self 代表实例本身,是实例方法的第一个参数
  • 类属性被所有实例共享,实例属性每个实例独立
  • @classmethod 定义类方法,@staticmethod 定义静态方法
  • Python 使用单下划线 _ 表示受保护,双下划线 __ 表示私有(名称修饰)

练习

  1. 定义一个 Rectangle 类,包含长和宽属性,以及计算面积和周长的方法。
  2. 定义一个 Circle 类,使用类属性存储 PI,提供计算面积和 circumference 的方法。
  3. 编写一个 BankAccount 类,支持存款、取款、查询余额,余额不能为负数。
  4. 使用 @classmethodBook 类添加一个从字典创建实例的方法。
  5. 解释 selfcls 的区别,并举例说明。