Skip to content
返回

Python 面向对象

AI 总结

本文全面介绍了Python面向对象编程的核心概念和特性,包括模块与包的使用、类与对象的定义、封装、继承、多态、私有属性、多重继承、定制类、枚举类和元类等内容,并通过代码示例展示了如何应用这些概念。

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. —— Martin Fowler

目录

模块和包

模块(module):一个 .py 文件(或已编译的扩展)即一个模块,里面放变量、函数、类、可执行语句。

包(package):一个包含多个模块的目录。现代 Python 中不要求必须有 __init__.py 才算包,但建议保留以便清晰与兼容。

Python 解释器在导入模块时,会按照一定的顺序搜索模块。

  1. 当前目录:首先搜索当前目录。
  2. PYTHONPATH 环境变量:搜索环境变量 PYTHONPATH 指定的目录列表。
  3. 标准库目录:搜索 Python 安装目录下的标准库。
  4. site-packages 目录:搜索第三方扩展的安装位置(通常是 site-packages 目录)。
  5. sys.path 中的其他路径:搜索通过 sys.path.append() 添加的自定义路径。
# 1) 创建 mymath.py
PI = 3.14159
def area(r): return PI * r * r

# 2) 在其他文件中导入
import mymath
print(mymath.area(2))

from mymath import area, PI
print(area(2), PI)

from mymath import *   # 不推荐,易污染命名空间

# 导入整个包
import my_package
my_package.module1.some_function()

# 导入特定子模块
import my_package.module1
my_package.module1.some_function()

# 导入包中 `__all__` 列表定义的所有内容
# 如果 `__init__.py` 中没有定义 `__all__`,则导入所有不以下划线开头的名称
from my_package import *
module1.some_function()

# 只导入特定模块
from my_package import module1
module1.some_function()
导入方式优点缺点适用场景
import my_package最明确,避免命名冲突代码较长大型项目,需要明确性
import my_package.module1明确且节省内存代码较长只需要特定模块时
from my_package import *代码简短可能冲突,不明确不推荐使用
from my_package import module1代码简洁可能冲突小型项目,明确知道模块内容
from .module1 import module1_function代码简洁可能冲突小型项目,明确知道模块内容

面向对象

核心概念

“像鸭子那样走、像鸭子那样叫,那它就可以当鸭子用。”

# 定义一个学生类
class Student:
    # __init__ 是Python的特殊方法,用于初始化对象,会在创建对象时自动调用
    # 初始化方法的第一个参数通常是self,表示对象本身
    # 初始化方法,接收名字和分数两个参数
    def __init__(self, name, score):
        # 将传入的名字赋值给实例变量name
        self.name = name
        # 将传入的分数赋值给实例变量score
        self.score = score
    # 方法的第一个参数通常是self,表示对象本身
    def print_score(self):
        # 使用f-string格式化输出学生的名字和分数
        print(f'{self.name}的分数是{self.score}')

# 创建一个名为"小明"、分数为90的学生实例
student1 = Student('小明', 90)
# 调用学生实例的print_score方法,打印小明的分数
student1.print_score()

# 定义一个银行账户类
class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # 私有属性,外部无法直接访问

    def deposit(self, amount):
        self.__balance += amount  # 存款方法

    def get_balance(self):
        return self.__balance  # 获取余额方法

bankAccount = BankAccount(1000)
print(bankAccount.get_balance())  # 输出: 1000
bankAccount.deposit(500)
print(bankAccount.get_balance())  # 输出: 1500

访问限制

让类的属性不被外部访问,可以把属性的名称前加上两个下划线 __,这样就变成了一个私有变量(private),只有内部可以访问,外部不能直接访问,通常会提供 gettersetter 方法来访问和修改私有变量。

# 定义一个学生类,使用私有变量
class Student:
    def __init__(self, name, score):
        self.__name = name  # 私有变量,外部无法直接访问
        self.__score = score  # 私有变量,外部无法直接访问

    def get_name(self):
        return self.__name  # 获取名字的方法

    def get_score(self):
        return self.__score  # 获取分数的方法

    def set_score(self, score):
        if 0 <= score <= 100:  # 检查分数是否有效
            self.__score = score  # 设置分数
        else:
            raise ValueError('分数必须在0到100之间')  # 抛出错误

# 创建学生对象
student = Student('小明', 90)  # 创建学生对象
print(student.get_name())  # 输出: 小明
print(student.get_score())  # 输出: 90

# 访问私有属性 不推荐
# 在 Python 中,以双下划线开头的属性会被自动改名为:_类名__属性名
# 可以通过 _Student__name 访问 __name 属
print(student._Student__name)  # 输出: 小明

继承

在 Python 3 中,class Student() 和 class Student 是完全等价的,括号是可选的,可以省略,如果类不继承任何其他类,可以完全不写括号,也可以在括号里写 object,表示继承自 object 类。

多态

多态是面向对象编程的一个重要特性,它允许不同类的对象对同一方法有不同的响应。多态可以让代码更灵活,新增子类时无需修改原有代码。

# 定义一个动物类
class Animal:
    def __init__(self, name):
        self.name = name  # 动物的名字
    def speak(self):
        print('动物发出声音')  # 动物发出声音的方法  

# 定义一个狗类,继承自动物类
class Dog(Animal):
    def speak(self):
		super().speak()  # 调用父类方法 speak
        return f'{self.name}说:汪汪!'  # 狗发出声音

# 定义一个猫类,继承自动物类
class Cat(Animal):
    def speak(self):
        return f'{self.name}说:喵喵!'  # 猫发出声音

# 创建狗对象
dog = Dog('旺财')  # 创建狗对象
print(dog.speak())  # 输出: 动物发出声音\n旺财说:汪汪!

# 创建猫对象
cat = Cat('咪咪')  # 创建猫对象
print(cat.speak())  # 输出: 咪咪说:喵喵!

类型判断

获取对象信息是一种了解对象类型、属性和方法的方式,帮助我们更好地操作对象。

isinstance(3, int)                # True
isinstance(True, bool)            # True
isinstance(True, int)             # True
isinstance([], (list, tuple))     # True 组合判断

# type 精确类型
type(True) is int  # False — isinstance(True, int) 为 True

# 判断函数类型
import types
def fn():
    pass
# 判断fn是否为函数类型
type(fn) == types.FunctionType  # True
# 判断abs是否为内置函数类型
type(abs) == types.BuiltinFunctionType  # True

# 抽象基类
from collections.abc import Iterable
isinstance("abc", Iterable)  # True

getattr(obj, "name", default)   # obj.name,不存在给默认
setattr(obj, "name", 42)        # obj.name = 42
delattr(obj, "name")            # del obj.name
hasattr(obj, "name")
vars(obj)        # 或 obj.__dict__:实例属性字典(可能不存在,见 __slots__)
dir(obj)         # 可预览可用的属性名(包含继承/魔术名)

class User:
    def __init__(self, first, last):
        self.first, self.last = first, last

    @property
    def full(self):
        return f"{self.first} {self.last}"

# property 存在于类上;实例的 `__dict__` 里没有键 `full`
u = User("Ada", "Lovelace")
"full" in vars(u)          # False
type(User.full)            # <class 'property'>

魔术方法

常用于 定制类


# dunder methods
__new__(cls, *args, **kw)     # 创建实例(很少需要改)
__init__(self, ...)           # 初始化
__repr__(self)                # 机器可读表示(调试/交互优先用)
__str__(self)                 # 人类可读表示(print)
__bytes__(self)               # bytes() 转换

# 比较与哈希
__eq__ __ne__ __lt__ __le__ __gt__ __ge__  # ==, <, ...
__hash__(self)    # 可放入 set/dict 的键,需要与 __eq__ 一致

# 布尔与转换
__bool__(self)    # bool(obj)
__int__ __float__ __complex__ __index__ # int/float/complex/索引

__call__(self, *args, **kw) # obj() 像函数一样被调用
__enter__ __exit__          # with 语句
__await__ __aiter__ __anext__  # async/await & 异步迭代

__len__(self)               # len()
__iter__(self) __next__(self)   # 迭代
__getitem__(self, key)      # obj[key] / 切片
__setitem__(self, key, val)  # obj[key] = val
__delitem__(self, key)       # del obj[key]
__contains__(self, item)    # in

__getattr__(self, name)         # 仅当常规查找失败时触发(兜底)
__getattribute__(self, name)    # 每次属性访问都触发(非常强,慎用)
__setattr__(self, name, value)  # obj.name = val
__delattr__(self, name)         # del obj.name

# 描述符
__get__(self, obj, owner)  # obj.descriptor 或 owner.descriptor
__set__(self, obj, value)  # obj.descriptor = val
__delete__(self, obj)      # del obj.descriptor
# @property 就是用描述符实现的语法糖

# 算术位运算
__add__  __radd__  __iadd__     # +, 右操作数实现, +=
__sub__  __rsub__  __isub__      # -, 右操作数实现, -=
__mul__  __rmul__  __imul__      # *, 右操作数实现, *=
__matmul__ __rmatmul__ __imatmul__   # @, 右操作数实现, @=
__truediv__ __floordiv__ __mod__ __pow__ ...  # /, //, %, **
__and__ __or__ __xor__ __lshift__ __rshift__ ...  # &, |, ^, <<, >>

# 复制 序列化
__copy__ __deepcopy__                # copy 模块
__getstate__ __setstate__            # pickle 自定义

# 模式匹配
__match_args__ = ("x", "y")   # match/case 解构时的位置字段

# 自定义只读映射实例
class ReadOnlyMapping:
    def __init__(self, data): self._data = dict(data)

    # 显示与布尔
    def __repr__(self): return f"ReadOnlyMapping({self._data!r})"
    def __bool__(self): return bool(self._data)

    # 容器协议
    def __len__(self): return len(self._data)
    def __iter__(self): return iter(self._data)
    def __contains__(self, key): return key in self._data
    def __getitem__(self, key): return self._data[key]

    # 禁止写操作
    def __setitem__(self, key, val): raise TypeError("read-only")
    def __delitem__(self, key): raise TypeError("read-only")

m = ReadOnlyMapping({"a": 1, "b": 2})
len(m), "a" in m, m["b"]    # 2, True, 2
print(m)                    # ReadOnlyMapping({'a': 1, 'b': 2})
-- 2025 年 9 月 28 日止 --

属性

# 定义一个学生类
class Student:
    name = "Student"    # 类属性
    count = 0           # 类属性,用于统计学生人数

    def __init__(self, name):
        self.name = name    # 实例属性,每个学生可以有不同的名字
        Student.count += 1  # 每创建一个学生,count加1

# 创建学生对象
student1 = Student("小明")  # 创建学生对象
student2 = Student("小红")  # 创建学生对象

student1.count  # 输出: 2
Student.count   # 输出: 2
student1.name   # 输出: 小明
Student.name    # 输出: Student

__slots__

一种限制实例属性的机制,可以防止实例动态添加属性和方法。

__slots__ 定义的属性仅对当前类实例起作用,对继承的子类不起作用。

# 定义一个学生类,使用__slots__限制属性
class Student:
    __slots__ = ('name', 'age')  # 只允许实例绑定name和age属性

    def __init__(self, name, age):
        self.name = name  # 初始化name属性
        self.age = age  # 初始化age属性

# 创建学生对象
student = Student('小明', 20)  # 创建学生对象
print(student.name)  # 输出: 小明
print(student.age)  # 输出: 20
student.score = 90  # 错误,AttributeError: 'Student' object has no attribute 'score' and no __dict__ for setting new attributes

# 定义一个函数作为实例方法
def set_age(self, age):
    self.age = age  # 设置age属性

# 创建学生对象
student = Student()  # 创建学生对象
from types import MethodType  # 导入types模块中的MethodType类

# types模块提供了Python内置类型的访问方式
# MethodType用于创建绑定方法对象,将函数绑定到实例上
# 这里会报错:AttributeError: 'Student' object has no attribute 'set_age' and no __dict__ for setting new attributes
# 原因是我们使用了__slots__限制了属性,且没有包含'set_age'
# 使用MethodType将set_age函数绑定到student实例
student.set_age = MethodType(set_age, student) 

# 定义一个研究生类,继承自学生类
class GraduateStudent(Student):
    pass

# 创建研究生对象
graduate = GraduateStudent('小红', 25)  # 创建研究生对象
# 对继承的子类不起作用
graduate.score = 90  # 动态绑定score属性
print(graduate.score)  # 输出: 90

@property

@property 是一种装饰器,可以将方法转换为属性,让代码更简洁,同时保证参数检查。

# 定义一个学生类,使用@property装饰器
class Student:
    def __init__(self):
        self._score = 0  # 私有变量,外部无法直接访问

    # @property装饰器可以将一个方法转换为属性,使得我们可以像访问属性一样调用这个方法
    # 这样可以在不改变类接口的情况下,增加对属性的控制,比如添加验证逻辑
    # 在这个例子中,它使得我们可以通过student.score来获取分数,而不是调用student.score()
    @property
    def score(self):
        return self._score  # 获取分数的方法

    # @score.setter装饰器是用于设置属性的值
    # 它将一个方法转换为属性,使得我们可以像设置属性一样调用这个方法
    # 这样可以在不改变类接口的情况下,增加对属性的控制,比如添加验证逻辑
    # 在这个例子中,它使得我们可以通过student.score = 90来设置分数,而不是调用student.score(90)
    # score指的就是score方法,setter是固定写法,这个表示设置score方法的值
    # 不定义setter方法,可以创建只读属性
    @score.setter
    def score(self, value):
        if not isinstance(value, int):  # 检查参数类型
            raise ValueError('分数必须是整数!')  # 抛出错误
        if value < 0 or value > 100:  # 检查参数范围
            raise ValueError('分数必须在0到100之间!')  # 抛出错误
        self._score = value  # 设置分数

# 创建学生对象
student = Student()  # 创建学生对象
student.score = 90  # 设置分数
print(student.score)  # 输出: 90
student.score = 999  # ValueError: 分数必须在0到100之间!

多重继承

多重继承是一种允许子类同时继承多个父类的机制,让子类获得多个父类的所有功能。

# 语法
class 子类名(父类1, 父类2, ...):
    pass

# 定义一个动物类
class Animal:
    def __init__(self, name):
        self.name = name  # 动物的名字

# 定义一个可跑类
class Runnable:
    def run(self):
        print(f'{self.name}正在跑...')  # 跑的方法

# 定义一个可飞类
class Flyable:
    def fly(self):
        print(f'{self.name}正在飞...')  # 飞的方法

# 定义一个狗类,继承自动物类和可跑类
class Dog(Animal, Runnable):
    pass

# 定义一个蝙蝠类,继承自动物类和可飞类
class Bat(Animal, Flyable):
    pass

# 创建狗和蝙蝠对象
dog = Dog('旺财')  # 创建狗对象
bat = Bat('小蝙蝠')  # 创建蝙蝠对象
dog.run()  # 旺财正在跑...
bat.fly()  # 小蝙蝠正在飞...

定制类

让类像内置类型一样工作,融入语法/协议,主要就是通过魔术方法(dunder methods)实现的;此外还可以用 描述符、property__slots__、元类、dataclass 等机制进一步定制。

class Vector:
    def __init__(self, x, y): self.x, self.y = x, y
    def __repr__(self): return f"Vector({self.x}, {self.y})"
    def __add__(self, other): return Vector(self.x + other.x, self.y + other.y)
    def __len__(self): return 2
    def __iter__(self): yield from (self.x, self.y)

v1, v2 = Vector(1, 2), Vector(3, 4)
print(v1 + v2)         # Vector(4, 6)   ← 定制了加法
print(len(v1))         # 2              ← 定制了长度
x, y = v1              # 可解包/迭代

枚举类

枚举类是一种用于定义常量的方式

# 导入枚举类和unique装饰器
# unique装饰器 可以确保没有重复值
from enum import Enum, unique

# 定义一个月份枚举类
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))  # 定义月份枚举类

# 遍历月份枚举类
for name, member in Month.__members__.items():  # 遍历月份枚举类
    print(name, '=>', member, ',', member.value)  # 输出月份名称、成员和值

# Jan => Month.Jan , 1
# Feb => Month.Feb , 2
# ...
# ...
# Dec => Month.Dec , 12

# 定义一个星期枚举类
class Weekday(Enum):  # 定义星期枚举类
    Sun = 0  # 星期日
    Mon = 1  # 星期一
    Tue = 2  # 星期二
    Wed = 3  # 星期三
    Thu = 4  # 星期四
    Fri = 5  # 星期五
    Sat = 6  # 星期六

# 访问星期枚举类
day1 = Weekday.Mon  # 获取星期一
print(day1)  # 输出: Weekday.Mon
print(Weekday.Tue)  # 输出: Weekday.Tue
print(Weekday['Tue'])  # 输出: Weekday.Tue
print(Weekday.Tue.value)  # 输出枚举的值 2

# 定义一个性别枚举类
@unique  # 使用unique装饰器确保没有重复值
class Gender(Enum):  # 定义性别枚举类
    Male = 0  # 男性
    Female = 1  # 女性

# 定义一个学生类
class Student:  # 定义学生类
    def __init__(self, name, gender):  # 初始化方法
        self.name = name  # 学生名字
        self.gender = gender  # 学生性别

# 创建学生对象
bart = Student('Bart', Gender.Male)  # 创建学生对象
if bart.gender == Gender.Male:  # 判断性别
    print('测试通过!')  # 输出: 测试通过!
else:
    print('测试失败!')

# 定义一个颜色枚举类
@unique  # 使用unique装饰器确保没有重复值
class Color(Enum):  # 定义颜色枚举类
    Red = 1  # 红色
    Green = 2  # 绿色
    Blue = 3  # 蓝色

# 通过成员名称访问
print(Color.Red)        # 输出: Color.Red
# 通过成员值访问
print(Color.Red.value)  # 输出: 1
# 通过成员索引访问
print(Color['Red'])     # 输出: Color.Red

元类

在 Python 里,所有类对象默认都是由 type 这个元类创建的;也就是说:type 是“类的类”

class Foo(Bar):
    x = 1
    def f(self): ...

上面代码背后发生的事:

  1. 先执行类体代码,得到一个命名空间(dict);
  2. 选定元类(默认 type,或 metaclass=... 指定);
  3. 调用元类:Meta.__new__(Meta, name, bases, namespace) → 生成类对象;
  4. 接着 Meta.__init__(cls, name, bases, namespace) 做进一步初始化;
  5. 得到最终的类对象 Foo;之后用 Foo() 实例化时,会走 元类的 __call__,其默认流程是:__new____init__
class Point: x=0; y=0
# 等价于
Point = type("Point", (object,), {"x": 0, "y": 0})

p = Point()

class MyMeta(type):
    pass

# 指定元类
class A(metaclass=MyMeta):
    pass

isinstance(A, MyMeta)  # True:A 是由 MyMeta 这个“工厂”造出来的

# type 示例
# 定义一个函数,接受self参数和name参数(默认值为'world')
def fn(self, name='world'):
    # 使用f字符串格式化输出问候语
    print(f'Hello, {name}.')

# 使用type()函数动态创建一个名为Hello的类,继承自object,包含hello方法
Hello = type('Hello', (object,), {'hello': fn})

# 创建Hello类的实例对象
h = Hello()

# 调用实例对象的hello方法
h.hello()  # Hello, world.

# 打印Hello类的类型,显示它是一个type类的实例
print(type(Hello))  # <class 'type'>

# 打印实例对象h的类型,显示它是Hello类的实例
print(type(h))  # <class '__main__.Hello'>

# 使用 metaclass 创建类
# 定义一个名为ListMetaclass的元类,继承自type
class ListMetaclass(type):
    # 定义__new__方法,用于创建新的类对象
    # cls 是当前类 <class '__main__.ListMetaclass'>
    # name 是创建的类的名字 MyList
    # bases 是创建的类的父类 (<class 'list'>,)
    # attrs 是创建的类的属性字典 {'__module__': '__main__', '__qualname__': 'MyList'}
    def __new__(cls, name, bases, attrs):
        # 在类的属性字典中添加一个add方法,该方法调用list的append方法
        attrs['add'] = lambda self, value: self.append(value)
        # 调用type的__new__方法创建并返回新的类对象
        return type.__new__(cls, name, bases, attrs)

# 使用ListMetaclass元类创建一个名为MyList的类,继承自list
# list是Python内置的列表类型,这里MyList继承自list,获得了列表的所有功能
# metaclass=ListMetaclass 指定使用ListMetaclass作为元类来创建MyList类
class MyList(list, metaclass=ListMetaclass):
    # 空类体,不添加任何额外的方法或属性
    pass

# 创建MyList类的一个实例对象
L = MyList()
# 调用通过元类添加的add方法,向列表中添加元素1
L.add(1)
# 打印列表内容,输出结果为[1]
print(L)
-- 2025 年 9 月 29 日止 --

配套代码:vsme/learn-python



上一篇
Python I/O 编程与多任务
下一篇
Python 函数式编程