类与对象

类与对象

  • 类 (Class): 模板,定义属性和方法。
  • 对象 (Object): 类的实例 (Instance)。
# 定义类
class Person:
    """简单类示例"""
    species = "人类"  # 类变量

    def __init__(self, name: str, age: int):
        self.name = name       # 实例变量
        self.age = age

    def greet(self) -> str:    # 实例方法
        return f"Hi, I'm {self.name}, {self.age} years old."

# 创建对象
p = Person("Alice", 30)
print(p.greet())  # Hi, I'm Alice, 30 years old.
print(Person.species)  # 人类

要点:

  • __init__初始化方法, 在实例创建后被调用。
  • self 表示当前对象的引用(self虽不是关键字, 但约定俗成), 用来访问对象的属性和方法。方法调用时 Python 自动传入,不需要手动写。
  • 类变量 在所有实例间共享, 实例变量是每个实例独有的。

实例变量/类变量/私有变量
实例变量 (Instance Variables): 属于特定实例(对象)的变量,每个实例都拥有自己独立的一份副本。
特点:

  • __init__ 构造函数 或 其他实例方法中 通过 self.变量名 定义
  • 每个对象的 实例变量值 可以不同

类变量 (Class Variables): 属于类本身的变量,被所有实例共享。
特点:

  • 在 类内部 但 在所有方法之外 定义
  • 所有对象 共享同一份数据

私有变量 (Private Variables): 在变量名前加 双下划线 __ 的变量,Python 会对其进行名称修饰,实现一定程度的"私有化"。
特点:

  • Python 会将其重命名为 _类名__变量名
  • 只是一种约定,并非真正的私有(Python 没有真正的私有变量)

保护变量 (Protected Variables): 单下划线 _ 开头的变量。

class C:
   shared = []         # 类变量(可变,需小心)
   def __init__(self, x):
       self.x = x      # 实例变量
       self._hidden = 0   # 约定“受保护”
       self.__private = 1 # 名称改写(name mangling)

c = C(10)
print(c.shared)

私有变量 __name 会被改写为 _ClassName__name,用于避免子类覆盖(不是绝对私密)。

实例方法/类方法/静态方法
访问权限:

  • 实例方法:对实例和类都有完全访问权限。最常用、最灵活的方法。
  • 类方法:仅能看到所有类属性和其他类方法,看不到任何具体的实例。
  • 静态方法:与类完全隔离。它就像一个定义在类命名空间下的普通函数,不能直接使用 selfcls
class Student:
    school = "ABC School"

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

    def say_hi(self):  # 实例方法
        print(f"Hi, I am {self.name}")

    @classmethod
    def school_info(cls):  # 类方法
        print(f"School name: {cls.school}")

    @staticmethod
    def greet():  # 静态方法
        print("Welcome to our school!")

@classmethod 第一个参数是类 (cls);常用于 工厂方法访问/修改类状态
@staticmethod 不接收 selfcls;像普通函数,但放 在类命名空间下 便于组织。

封装 (Encapsulation)

封装的意义:

  • 隐藏实现细节,只暴露必要的接口
  • 保护数据不被外部直接访问和修改。

公有属性与方法

Python 默认所有属性和方法都是 公有 的,可以直接访问。

class Car:
    def __init__(self, brand):
        self.brand = brand  # 公有属性

    def drive(self):  # 公有方法
        print(f"{self.brand} is driving")

私有属性与方法

  • 私有属性/方法以 双下划线 __ 开头。
  • 外部不能直接访问 (Python 会做名称改写)。
class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # 私有属性

    def deposit(self, amount):
        self.__balance += amount

    def get_balance(self):  # 提供公有方法访问
        return self.__balance

acc = BankAccount(100)
acc.deposit(50)
print(acc.get_balance())  # 150
print(acc.__balance)      # ❌ 报错: AttributeError

@property 装饰器

用来实现 getter 和 setter,让属性像访问变量一样。

class Person:
    def __init__(self, age):
        self.__age = age

    @property
    def age(self):  # getter
        return self.__age

    @age.setter
    def age(self, value):  # setter
        if value < 0:
            raise ValueError("Age cannot be negative")
        self.__age = value

p = Person(18)
print(p.age)   # 调用 getter
p.age = 20     # 调用 setter

继承 (Inheritance)

子类 (Subclass) 可以继承 父类 (Superclass) 的 属性和方法,并且可以 扩展 或 重写。

继承的优缺点:

  • 优点: 代码复用, 层次结构清晰(表达“是一个 (is-a)”的关系)
  • 缺点: 耦合度高(父类改动可能影响所有子类)

建议: 优先 组合 (Composition),再考虑 继承

单继承

子类 Dog 继承了 Animal 的属性,并重写了 speak() 方法:

class Animal:  # 父类
    def __init__(self, name):
        self.name = name

    def speak(self):  # 父类方法
        print("Animal makes a sound")

class Dog(Animal):  # 子类
    def speak(self):  # 重写方法
        print(f"{self.name} says Woof!")

dog = Dog("Buddy")
dog.speak()  # Buddy says Woof!

super() 的用法: super() 用来调用父类的方法,常见于构造函数中。

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

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # 调用父类构造方法
        self.breed = breed

dog = Dog("Buddy", "Labrador")
print(dog.name, dog.breed)

多重继承

Python 支持多重继承,一个类可以继承多个父类。

class A:
    def say(self):
        print("A")

class B:
    def say(self):
        print("B")

class C(A, B):
    pass

c = C()
c.say()
# 类名.mro() 查看 MRO 方法解析顺序
print(C.mro()) 
# 结果: 
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

因为 Python 采用 MRO (Method Resolution Order, 方法解析顺序),会优先搜索 A。

多态 (Polymorphism)

多态 (Polymorphism): 相同的接口,不同的对象表现出不同的行为。
好处: 增加代码的 灵活性 和 可扩展性

方法重写 (Override)

子类可以重写父类的方法,实现不同的功能。

相同的方法名 speak(),不同对象表现不同:

class Animal:
    def speak(self):
        print("Animal speaks")

class Dog(Animal):
    def speak(self):
        print("Woof!")

class Cat(Animal):
    def speak(self):
        print("Meow!")

animals = [Dog(), Cat()]
for animal in animals:
    animal.speak()

鸭子类型 (Duck Typing)

Python 的多态不要求必须继承同一个父类,只要对象实现了同样的方法,就可以当作同类使用。
“如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。”

class Duck:
    def speak(self):
        print("Quack!")

class Person:
    def speak(self):
        print("Hello!")

def make_it_speak(obj):
    obj.speak()

make_it_speak(Duck())    # Quack!
make_it_speak(Person())  # Hello!

抽象基类 (ABC)

有时候我们希望强制子类必须实现某些方法,可以用抽象基类。

from abc import ABC, abstractmethod

class Animal(ABC):  # 抽象类
    @abstractmethod
    def speak(self):  # 抽象方法
        pass

class Dog(Animal):
    def speak(self):
        print("Woof!")

# a = Animal()  # ❌ 报错: 不能实例化抽象类
dog = Dog()
dog.speak()

类的高级特性

魔术方法 (Magic Methods)

魔术方法是 Python 内置的一组特殊方法,名字前后都有两个下划线,比如 __init____str__

魔术方法 让类的对象表现得更 “像内置类型”,比如:

  • __init__ 初始化对象
  • __str__ 打印对象时调用
  • __len__ 让对象可以用 len(obj)
  • __getitem__ 让对象可以像列表一样用 obj[i]
  • __add__ 让对象支持 + 运算符
  • __lt__ 让对象支持 < 运算符
  • __contains__ 让对象支持 in 运算符
  • __call__ 让对象可以像函数一样调用

好处: 让自定义类更加“Pythonic”,像内置类型一样好用。

对象的创建与销毁

  • __new__: 控制对象的创建 (通常用在实现单例模式)
  • __init__: 控制对象的初始化 (最常见)
  • __del__: 控制对象的销毁 (不建议使用, 因为 Python 的垃圾回收不保证立即调用)。
class Person:
    def __new__(cls, *args, **kwargs):
        print("Creating instance")
        return super().__new__(cls)

    def __init__(self, name):
        print("Initializing instance")
        self.name = name

p = Person("Alice")
# 输出:
# Creating instance
# Initializing instance

对象的字符串表示

  • __str__: 用于 print(obj),面向用户,返回可读性强的字符串。
  • __repr__: 用于调试,面向开发者,返回更精确的字符串。
class Person:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f"Person(name={self.name})"

    def __repr__(self):
        return f"<Person: {self.name}>"

p = Person("Alice")
print(p)   # Person(name=Alice)
p          # <Person: Alice>

容器类相关魔术方法

这些方法让自定义类像列表/字典一样使用:

class MyList:
    def __init__(self, data):
        self.data = data

    def __len__(self):  # 让对象支持 len()
        return len(self.data)

    def __getitem__(self, index):  # 支持 obj[i]
        return self.data[index]

    def __setitem__(self, index, value):  # 支持 obj[i] = value
        self.data[index] = value

    def __iter__(self):  # 支持 for 循环
        return iter(self.data)

lst = MyList([1, 2, 3])
print(len(lst))     # 3
print(lst[0])       # 1
lst[1] = 10
print(lst.data)     # [1, 10, 3]
for x in lst:
    print(x)

运算符重载 (Operator Overloading)

Python 允许我们通过魔术方法重载运算符。

常见的运算符对应的方法:

运算符魔术方法
+__add__
-__sub__
*__mul__
/__truediv__
//__floordiv__
%__mod__
**__pow__
==__eq__
!=__ne__
<__lt__
<=__le__
>__gt__
>=__ge__

可迭代对象与迭代器

  • __iter__: 返回一个迭代器对象
  • __next__: 迭代器对象的方法,用于获取下一个元素
class CountDown:
    def __init__(self, start):
        self.start = start

    def __iter__(self):
        return self

    def __next__(self):
        if self.start <= 0:
            raise StopIteration
        self.start -= 1
        return self.start

cd = CountDown(5)
for num in cd:
    print(num)

# 输出:
# 4
# 3
# 2
# 1
# 0

模块化与包 (Modules & Packages)

模块是 .py 文件,包是带 __init__.py 文件 的文件夹。
最佳实践:
在包的 init.py 中定义对外暴露的 API。

模块 (Module)

模块 就是一个 Python 文件 (.py),里面包含变量、函数、类等定义。
模块的作用: 代码复用 和 代码组织。

创建一个 my_module.py 文件,内容如下:

# my_module.py
def greet(name):
    return f"Hello, {name}!"

pi = 3.14

在另一个文件中使用:

import my_module

print(my_module.greet("Alice"))  # Hello, Alice!
print(my_module.pi)              # 3.14

模块的导入方式

标准导入:

import my_module

print(my_module.pi)  # 3.14

导入时重命名 (别名):

import my_module as mm

print(mm.pi)  # 3.14

从模块中导入指定成员:

from my_module import greet, pi

print(greet("Bob"))  # Hello, Bob!
print(pi)            # 3.14

导入所有成员 (不推荐):

from my_module import *

print(greet("cx"))  # Hello, cx!
print(pi)                # 3.14

无论 import 多少次,一个模块只会在第一次加载时执行。后续的导入都不会重新执行模块代码。

name == “main” 机制

当一个 Python 文件被直接运行时,__name__ 的值是 “__main__"。
当它被作为模块导入时,__name__ 的值是模块名。

# my_module.py
def greet(name):
    return f"Hello, {name}!"

pi = 3.14

if __name__ == "__main__":
    print("This module is being run directly")
else:
    print("This module is being imported")

Python 内置模块

Python 内置了很多常用模块,可以直接导入:

  • math (数学函数)
  • os (操作系统接口)
  • sys (解释器相关)
  • random (随机数)
  • datetime (日期和时间)
  • json (JSON 编码/解码)
  • urllib (URL 处理)
  • requests (HTTP 请求)

例子:

import math

print(math.pi)  # 3.141592653589793

import random

print(random.randint(1, 10))  # 随机整数
print(random.choice(["a", "b", "c"]))  # 随机选择

import datetime

print(datetime.datetime.now())  # 当前时间

包 (Package)

是一个 包含多个 模块文件夹,并且这个文件夹里必须有一个 __init__.py 文件 (Python 3.3+ 可以省略,但建议保留)。

作用: 让 Python 能够更好地组织、管理大型项目的代码

包的结构示例:

my_project/
├── my_package/
│   ├── __init__.py
│   ├── module1.py
│   └── module2.py
└── main.py

包的使用:

# module1.py
def foo():
    print("This is foo() from module1")

# module2.py
def bar():
    print("This is bar() from module2")

# __init__.py
from .module1 import foo
from .module2 import bar

然后在 main.py 中使用:

from my_package import foo, bar

foo()
bar()

包的导入方式

绝对导入 (推荐)

from my_package import module1, module2

相对导入 (包内使用)

from . import module1, module2

from .module1 import foo
from .module2 import bar

from ..other_package.moduleX import something

模块搜索路径

当导入一个模块时,Python 会在以下几个位置搜索:
1. 当前目录
2. PYTHONPATH 环境变量指定的目录
3. Python 标准库目录
4. 第三方库目录

可以通过 sys.path 查看搜索路径:

import sys

print(sys.path)

第三方模块管理

安装第三方模块:

pip install requests

使用第三方模块:

import requests

response = requests.get("https://www.baidu.com")
print(response.status_code)  # 200

requirements.txt

requirements.txt 是一种约定:一个文本文件,列出项目运行时(或某个环境)所需的 Python 包及其版本约束。
作用: 重复部署时,确保使用的是相同的 Python 版本和依赖版本,以避免不一致的问题。

文件格式简单:每行一个依赖,可以包含版本约束、来源、注释等。
示例(简单):

# requirements.txt
requests==2.25.1

生成 / 更新 requirements.txt:

# 把当前环境所有(包含传递依赖)精确锁定(适合生产部署的“快照”文件)。
pip freeze > requirements.txt

安装 requirements.txt 中的依赖:

pip install -r requirements.txt