【Python基础篇-07】深入理解python的面向对象编程

深入理解python的面向对象编程

  • 一、面向对象编程
    • [1. 类与对象](#1. 类与对象)
      • [1.1 什么是类](#1.1 什么是类)
      • [1.2 创建对象](#1.2 创建对象)
      • [1.3 对象保存自己的数据](#1.3 对象保存自己的数据)
    • [2. 初始化方法与 self](#2. 初始化方法与 self)
      • [2.1 使用init初始化对象](#2.1 使用init初始化对象)
      • [2.2 self 表示当前实例](#2.2 self 表示当前实例)
      • [2.3 不要遗漏 self](#2.3 不要遗漏 self)
      • [2.4 init不返回业务结果](#2.4 init不返回业务结果)
    • [3. 实例属性与类属性](#3. 实例属性与类属性)
      • [3.1 实例属性:每个对象各自拥有](#3.1 实例属性:每个对象各自拥有)
      • [3.2 类属性:定义在类本身上的数据](#3.2 类属性:定义在类本身上的数据)
      • [3.3 实例赋值可能遮蔽类属性](#3.3 实例赋值可能遮蔽类属性)
      • [3.4 不要把每个实例的数据放进可变类属性](#3.4 不要把每个实例的数据放进可变类属性)
    • [4. 实例方法、类方法与静态方法](#4. 实例方法、类方法与静态方法)
      • [4.1 实例方法](#4.1 实例方法)
      • [4.2 类方法](#4.2 类方法)
      • [4.3 静态方法](#4.3 静态方法)
      • [4.4 三种方法对比](#4.4 三种方法对比)
    • [5. 属性管理与 property](#5. 属性管理与 property)
      • [5.1 直接公开属性的问题](#5.1 直接公开属性的问题)
      • [5.2 使用 property 校验赋值](#5.2 使用 property 校验赋值)
      • [5.3 避免在 property 中递归访问自己](#5.3 避免在 property 中递归访问自己)
      • [5.4 只读属性](#5.4 只读属性)
      • [5.5 单下划线与双下划线](#5.5 单下划线与双下划线)
    • [6. 继承与方法重写](#6. 继承与方法重写)
      • [6.1 继承已有行为](#6.1 继承已有行为)
      • [6.2 方法重写](#6.2 方法重写)
      • [6.3 super() 的含义](#6.3 super() 的含义)
      • [6.4 isinstance 与 type](#6.4 isinstance 与 type)
    • [7. 多态与鸭子类型](#7. 多态与鸭子类型)
      • [7.1 相同操作,不同实现](#7.1 相同操作,不同实现)
      • [7.2 使用抽象基类明确约定](#7.2 使用抽象基类明确约定)
    • [8. 组合:让对象协作](#8. 组合:让对象协作)
      • [8.1 继承与组合的区别](#8.1 继承与组合的区别)
      • [8.2 组合示例](#8.2 组合示例)
    • [9. 常用特殊方法](#9. 常用特殊方法)
      • [9.1 str:面向使用者的字符串](#9.1 str:面向使用者的字符串)
      • [9.2 repr:便于调试的表示](#9.2 repr:便于调试的表示)
      • [9.3 len:支持 len()](#9.3 len:支持 len())
      • [9.4 eq:定义相等规则](#9.4 eq:定义相等规则)
    • [10. dataclass:简化数据类](#10. dataclass:简化数据类)
      • [10.1 自动生成常见方法](#10.1 自动生成常见方法)
      • [10.2 类型注解不等于运行时校验](#10.2 类型注解不等于运行时校验)
      • [10.3 可变默认值使用 default_factory](#10.3 可变默认值使用 default_factory)
      • [10.4 frozen=True](#10.4 frozen=True)

如需转载,请附上链接:https://zhenghuisheng.blog.csdn.net/article/details/166797802

一、面向对象编程

前几篇,我们已经学习了数据容器、函数、异常处理和模块组织,并完成了一个能够读取、筛选和统计商品的程序。在这个程序中,商品数据保存在字典里,查询和修改操作放在函数中:

python 复制代码
product = {
    "name": "矿泉水",
    "price": 2,
    "stock": 100
}

这种方式简单直接,适合很多小型程序。但当一个商品需要维护更多规则时,例如库存不能为负数、出库不能超过现有库存、名称不能为空,就需要考虑如何把数据和相关操作组织起来。

面向对象提供了另一种组织方式:用对象保存状态,用方法描述操作,用类定义这类对象共同遵循的结构和行为。

1. 类与对象

1.1 什么是类

类用于定义一类对象共同具有的数据和行为。例如,商品通常具有:名称、单价、库存 ;商品还可以执行一些操作:增加库存、减少库存、计算指定数量的金额 可以使用 class 定义一个商品类:

python 复制代码
class Product:
    pass

类名通常使用大驼峰命名,例如:

复制代码
Product
ShoppingCart
InventoryService

1.2 创建对象

定义类后,通过 类名() 创建实例:

python 复制代码
class Product:
    pass

if __name__ == '__main__':
    first = Product()
    second = Product()
    print(type(first))
    print(first is second)

运行结果:

复制代码
<class '__main__.Product'>
False

first 和 second 都是 Product 的实例,但它们是两个不同的对象。几个概念可以这样区分:

概念 含义
类 定义对象共同的结构和行为
对象 程序中具体存在的实体
实例 强调某个对象是由某个类创建的

1.3 对象保存自己的数据

普通自定义类的实例,通常允许直接添加属性:

python 复制代码
class Product:
    pass

if __name__ == '__main__':
    first = Product()
    first.name = "矿泉水"
    first.stock = 100

    second = Product()
    second.name = "可乐"
    second.stock = 20

    print(first.name, first.stock)
    print(second.name, second.stock)

运行结果:

复制代码
矿泉水 100
可乐 20

但这种写法要求调用方手动补齐属性,容易遗漏,也容易拼错名称。

2. 初始化方法与 self

2.1 使用init初始化对象

python 复制代码
class Product:
    def __init__(self, name, price, stock):
        self.name = name
        self.price = price
        self.stock = stock

if __name__ == '__main__':
    product = Product("矿泉水", 2, 100)
    print(product.name)
    print(product.price)
    print(product.stock)

运行结果:

复制代码
矿泉水
2
100

Product("矿泉水", 2, 100)

时,Python 通常先创建实例,再调用 __init__() 初始化它。严格来说,__init__ 负责初始化,不负责分配和创建实例本身。实例创建涉及 __new__,一般业务类暂时不需要自定义它。

2.2 self 表示当前实例

复制代码
self.name = name

可以拆成两部分:

  • 右侧 name:本次调用传入的参数。
  • 左侧 self.name:当前对象上的属性。

self 是实例方法的第一个参数,调用时由 Python 自动传入当前实例。

python 复制代码
class Product:
    def __init__(self, name):
        self.name = name

    def show_name(self):
        print(self.name)

if __name__ == '__main__':
    product = Product("矿泉水")
    product.show_name()

运行结果:

复制代码
矿泉水

对于这里的普通实例方法:

复制代码
product.show_name()

可以近似理解为:

复制代码
Product.show_name(product)

因此,调用时不需要再手动传入 self。self 不是关键字,但它是约定俗成的名称,不应随意改成其他名字。

2.3 不要遗漏 self

错误写法:

python 复制代码
class Product:
    def show_name():
        print("商品")

通过实例调用时,Python 会自动传入实例,但函数没有参数接收,因而出现 TypeError。正确写法:

python 复制代码
class Product:
    def show_name(self):
        print("商品")

2.4 init不返回业务结果

__init__() 应返回 None,通常不写 return。不要写成:

python 复制代码
class Product:
    def __init__(self, name):
        self.name = name
        return name

这会导致 TypeError。如果需要返回其他计算结果,应定义普通方法;如果需要提供另一种对象创建方式,可以使用后面介绍的类方法。

3. 实例属性与类属性

3.1 实例属性:每个对象各自拥有

通过 self.xxx 赋值,通常会建立实例属性:

python 复制代码
class Product:
    def __init__(self, name, stock):
        self.name = name
        self.stock = stock
        
if __name__ == '__main__':
    first = Product("矿泉水", 100)
    second = Product("可乐", 20)
    first.stock = 80
    print(first.stock)
    print(second.stock)

运行结果:

复制代码
80
20

修改第一个商品的库存,不会改变第二个商品的库存。

3.2 类属性:定义在类本身上的数据

python 复制代码
class Product:
    unit = "件"

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

if __name__ == '__main__':
    first = Product("矿泉水")
    second = Product("可乐")
    print(Product.unit)
    print(first.unit)
    print(second.unit)

运行结果:

复制代码
件
件
件

unit 定义在类体中,是类属性。对于这里的普通属性,实例没有同名属性时,可以通过实例访问类属性。

3.3 实例赋值可能遮蔽类属性

python 复制代码
class Product:
    unit = "件"

if __name__ == '__main__':
    first = Product()
    second = Product()
    first.unit = "箱"
    print(first.unit)
    print(second.unit)
    print(Product.unit)
    Product.unit = "瓶"
    print(first.unit)
    print(second.unit)

运行结果:

复制代码
箱
件
件
箱
瓶

first.unit = "箱" 在 first 上建立了同名实例属性,遮蔽了通过它访问到的类属性。之后修改 Product.unit:

  • second 没有自己的 unit,所以读到新的类属性。
  • first 已有自己的 unit,仍然返回 "箱"。

后面介绍的 property 具有专门的属性访问规则,不应简单套用普通属性的遮蔽方式。

3.4 不要把每个实例的数据放进可变类属性

下面的代码容易产生共享数据问题:

python 复制代码
class ShoppingCart:
    items = []
    def add(self, name):
        self.items.append(name)

if __name__ == '__main__':
    first = ShoppingCart()
    second = ShoppingCart()

    first.add("矿泉水")

    print(first.items)
    print(second.items)

运行结果:

复制代码
['矿泉水']
['矿泉水']

两个对象访问的是同一个类级列表。如果购物车内容应当各自独立,就应该在 __init__ 中创建:

python 复制代码
class ShoppingCart:
    def __init__(self):
        self.items = []

    def add(self, name):
        self.items.append(name)


if __name__ == '__main__':
    first = ShoppingCart()
    second = ShoppingCart()

    first.add("矿泉水")

    print(first.items)
    print(second.items)

运行结果:

复制代码
['矿泉水']
[]

4. 实例方法、类方法与静态方法

4.1 实例方法

实例方法接收 self,适合读取或修改某个对象的状态。

python 复制代码
class Product:
    def __init__(self, name, stock=0):
        self.name = name
        self.stock = stock
        
    def restock(self, quantity):
        if type(quantity) is not int or quantity <= 0:
            raise ValueError("入库数量必须是正整数")
        self.stock += quantity

if __name__ == '__main__':
    product = Product("矿泉水", 10)
    product.restock(5)
    print(product.stock)

运行结果:

复制代码
15

方法中的 return、参数、异常处理等规则,与普通函数一致。

4.2 类方法

使用 @classmethod 定义类方法,第一个参数通常命名为 cls,表示接收的类。

python 复制代码
class Product:
    def __init__(self, name, stock=0):
        self.name = name
        self.stock = stock

    @classmethod
    def from_dict(cls, data):
        return cls(
            name=data["name"],
            stock=data.get("stock", 0)
        )

if __name__ == '__main__':
    data = {
        "name": "矿泉水",
        "stock": 100
    }
    product = Product.from_dict(data)
    print(product.name)
    print(product.stock)

运行结果:

复制代码
矿泉水
100

这里的:

复制代码
return cls(...)

表示通过当前接收的类创建实例。类方法常用于提供额外的创建入口,例如:

复制代码
Product.from_dict(...)
Product.from_text(...)

@classmethod 是一种装饰器写法。

4.3 静态方法

使用 @staticmethod 定义静态方法,它不会自动接收 self 或 cls。

python 复制代码
class Product:
    @staticmethod
    def is_valid_name(name):
        return isinstance(name, str) and bool(name.strip())


if __name__ == '__main__':
    print(Product.is_valid_name("矿泉水"))
    print(Product.is_valid_name("   "))

运行结果:

复制代码
True
False

静态方法适合放置与类概念紧密相关、但不依赖实例或类状态的辅助逻辑。

4.4 三种方法对比

方法类型 自动接收 常见用途
实例方法 self,当前实例 读取、修改对象状态
类方法 cls,当前类 提供替代创建方式、处理类级逻辑
静态方法 无 与类相关的独立辅助操作

5. 属性管理与 property

5.1 直接公开属性的问题

下面的商品对象没有保护库存规则:

python 复制代码
class Product:
    def __init__(self, stock):
        self.stock = stock

外部可以直接写:

python 复制代码
product = Product(10)
product.stock = -100

如果库存不允许为负数,这个对象就进入了不合法状态。可以通过 property 在保留属性访问形式的同时,增加读取和赋值逻辑。

5.2 使用 property 校验赋值

python 复制代码
class Product:
    def __init__(self, name, stock=0):
        self.name = name
        self.stock = stock

    @property
    def stock(self):
        return self._stock

    @stock.setter
    def stock(self, value):
        if type(value) is not int or value < 0:
            raise ValueError("库存必须是非负整数")

        self._stock = value


if __name__ == '__main__':
    product = Product("矿泉水", 10)

    print(product.stock)

    product.stock = 20
    print(product.stock)

    try:
        product.stock = -1
    except ValueError as error:
        print(error)

    print(product.stock)

运行结果:

复制代码
10
20
库存必须是非负整数
20

这里:

  • 读取 product.stock 时,调用 getter。
  • 执行 product.stock = value 时,调用 setter。
  • 实际数据保存在 _stock 中。
  • 校验失败发生在赋值之前,因此原库存保持不变。

初始化中的:

复制代码
self.stock = stock

也会经过 setter,避免初始化时绕过校验。

5.3 避免在 property 中递归访问自己

错误写法:

python 复制代码
@property
def stock(self):
    return self.stock

读取 self.stock 又会调用 getter,形成不断递归。setter 中也不能这样写:

python 复制代码
@stock.setter
def stock(self, value):
    self.stock = value

因此通常使用另外一个内部属性保存数据:

python 复制代码
self._stock

5.4 只读属性

只定义 getter,不定义 setter,可以提供只读的属性接口。

python 复制代码
class Product:
    def __init__(self, name, price, stock):
        self.name = name
        self.price = price
        self.stock = stock

    @property
    def inventory_value(self):
        return self.price * self.stock


if __name__ == '__main__':
    product = Product("矿泉水", 2, 100)
    print(product.inventory_value)
    product.stock = 80
    print(product.inventory_value)

运行结果:

复制代码
200
160

inventory_value 每次读取时根据当前状态计算,不需要额外维护一份容易过期的数据。不能直接赋值:

复制代码
product.inventory_value = 999

因为没有 setter,会抛出 AttributeError。不过,"只读属性"不等于整个对象不可变。这里仍然可以修改 price 和 stock。

5.5 单下划线与双下划线

Python 中常见的名称约定:

写法 含义
name 普通公开名称
_name 约定为内部实现,外部通常不直接使用
__name 在类中触发名称改写,主要用于减少继承时的意外冲突
__name__ 特殊方法或特殊属性的命名形式

单下划线不是访问权限控制:

复制代码
product._stock

在普通情况下仍然能够被外部访问。双下划线也不是严格的安全隔离:

python 复制代码
class Example:
    def __init__(self):
        self.__value = 10

这个名称会被改写为类似:

复制代码
_Example__value

6. 继承与方法重写

6.1 继承已有行为

继承允许一个类复用另一个类的行为。

python 复制代码
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    def describe(self):
        return f"{self.name},单价 {self.price} 元"

class DigitalProduct(Product):
    def __init__(self, name, price, download_url):
        super().__init__(name, price)
        self.download_url = download_url

if __name__ == '__main__':
    product = DigitalProduct(
        "Python 学习资料",
        20,
        "https://example.com/python"
    )

    print(product.describe())
    print(product.download_url)

运行结果:

复制代码
Python 学习资料,单价 20 元
https://example.com/python

DigitalProduct(Product) 表示 DigitalProduct 继承 Product。

复制代码
super().__init__(name, price)

调用继承链上的初始化逻辑,避免重复设置 name 和 price。如果子类自定义了 __init__,需要的父类初始化不会自动再执行,应明确调用。

6.2 方法重写

子类可以重新定义同名方法:

python 复制代码
class Product:
    def __init__(self, name):
        self.name = name

    def delivery_info(self):
        return "需要安排实物配送"

class DigitalProduct(Product):
    def delivery_info(self):
        return "通过下载链接交付"


if __name__ == '__main__':
    first = Product("矿泉水")
    second = DigitalProduct("Python 学习资料")

    print(first.delivery_info())
    print(second.delivery_info())

运行结果:

复制代码
需要安排实物配送
通过下载链接交付

调用形式相同,但不同类型的对象提供了不同实现。

6.3 super() 的含义

在简单单继承中,可以先把 super() 理解为访问继承链上的下一层实现。

但它并不只是"写死调用某个父类"。Python 支持多继承,super() 会按照方法解析顺序,也就是 MRO,继续查找。。

6.4 isinstance 与 type

python 复制代码
class Product:
    pass

class DigitalProduct(Product):
    pass

if __name__ == '__main__':
    product = DigitalProduct()
    print(isinstance(product, DigitalProduct))
    print(isinstance(product, Product))
    print(type(product) is Product)

运行结果:

复制代码
True
True
False
  • isinstance() 考虑继承关系。
  • type(obj) is SomeClass 检查对象是否恰好属于这个类。

需要允许子类参与时,isinstance() 通常更合适。

7. 多态与鸭子类型

7.1 相同操作,不同实现

如果多个对象都提供某个方法,调用方就可以按照同一种方式使用它们。

python 复制代码
class ConsoleNotifier:
    def send(self, message):
        print(f"控制台通知:{message}")

class MemoryNotifier:
    def __init__(self):
        self.messages = []

    def send(self, message):
        self.messages.append(message)

def notify_low_stock(notifier, product_name):
    notifier.send(f"{product_name} 库存不足")

if __name__ == '__main__':
    console = ConsoleNotifier()
    memory = MemoryNotifier()
    notify_low_stock(console, "矿泉水")
    notify_low_stock(memory, "矿泉水")

    print(memory.messages)

运行结果:

复制代码
控制台通知:矿泉水 库存不足
['矿泉水 库存不足']

notify_low_stock() 不需要知道传入对象的具体类,只需要它提供能够按约定调用的 send()。这就是 Python 中常说的鸭子类型:关注对象能提供什么行为,而不只是它继承自哪个类。

7.2 使用抽象基类明确约定

如果需要明确要求子类实现某个方法,可以使用标准库 abc:

python 复制代码
from abc import ABC, abstractmethod

class Notifier(ABC):
    @abstractmethod
    def send(self, message):
        """发送消息。"""
class ConsoleNotifier(Notifier):
    def send(self, message):
        print(f"通知:{message}")
if __name__ == '__main__':
    notifier = ConsoleNotifier()
    notifier.send("库存不足")

运行结果:

复制代码
通知:库存不足

直接实例化仍有抽象方法未实现的类,会抛出 TypeError。抽象基类用于明确接口约定,但不会自动验证实现是否满足所有业务语义。小型程序中,如果鸭子类型已经足够清楚,不必为每个类都增加抽象层。

8. 组合:让对象协作

8.1 继承与组合的区别

继承通常表达"是一种"关系:

复制代码
数字商品是一种商品

组合通常表达"拥有或使用"关系:

复制代码
库存服务使用通知器

当一个对象只是需要借助另一个对象完成工作,组合通常更直接。

8.2 组合示例

python 复制代码
class ConsoleNotifier:
    def send(self, message):
        print(f"通知:{message}")


class InventoryService:
    def __init__(self, notifier):
        self.notifier = notifier

    def check_stock(self, product_name, stock):
        if stock < 5:
            self.notifier.send(f"{product_name} 库存不足")


if __name__ == '__main__':
    notifier = ConsoleNotifier()
    service = InventoryService(notifier)
    service.check_stock("矿泉水", 3)

运行结果:

复制代码
通知:矿泉水 库存不足

InventoryService 没有继承通知器,只是在内部保存并使用它。

以后换成别的通知实现,只要仍然提供兼容的 send(),库存服务就不必跟着修改

9. 常用特殊方法

9.1 str:面向使用者的字符串

直接打印普通对象时,默认结果通常包含类名和地址信息,不方便阅读。

可以定义 __str__():

python 复制代码
class Product:
    def __init__(self, name, stock):
        self.name = name
        self.stock = stock

    def __str__(self):
        return f"{self.name},库存 {self.stock}"


if __name__ == '__main__':
    product = Product("矿泉水", 100)

    print(product)
    print(str(product))

运行结果:

复制代码
矿泉水,库存 100
矿泉水,库存 100

__str__() 必须返回字符串,而不是直接打印内容。

9.2 repr:便于调试的表示

python 复制代码
class Product:
    def __init__(self, name, stock):
        self.name = name
        self.stock = stock

    def __str__(self):
        return f"{self.name},库存 {self.stock}"

    def __repr__(self):
        return f"Product(name={self.name!r}, stock={self.stock!r})"


if __name__ == '__main__':
    product = Product("矿泉水", 100)

    print(product)
    print(repr(product))
    print([product])

运行结果:

复制代码
矿泉水,库存 100
Product(name='矿泉水', stock=100)
[Product(name='矿泉水', stock=100)]

可以这样区分:

方法 常见用途
__str__ 面向使用者的简洁展示
__repr__ 面向开发者的调试表示

容器展示内部对象时通常使用其 repr 表示。

如果没有定义 __str__,默认字符串展示可以退回使用 __repr__。

9.3 len:支持 len()

python 复制代码
class ShoppingCart:
    def __init__(self):
        self.items = []

    def add(self, name):
        self.items.append(name)

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


if __name__ == '__main__':
    cart = ShoppingCart()
    cart.add("矿泉水")
    cart.add("饼干")

    print(len(cart))

运行结果:

复制代码
2

__len__() 应返回非负整数。

对于没有自定义 __bool__() 的对象,Python 也可以根据 __len__() 的结果判断真假:

复制代码
if cart:
    print("购物车有商品")

9.4 eq:定义相等规则

普通自定义类如果不定义相等逻辑,两个分别创建的实例即使属性一样,也通常不会比较为相等。

python 复制代码
class Product:
    def __init__(self, product_id, name):
        self.product_id = product_id
        self.name = name

    def __eq__(self, other):
        if not isinstance(other, Product):
            return NotImplemented

        return self.product_id == other.product_id


if __name__ == '__main__':
    first = Product(1001, "矿泉水")
    second = Product(1001, "矿泉水")

    print(first == second)
    print(first is second)

运行结果:

复制代码
True
False

NotImplemented 是一个特殊返回值,表示当前方法不负责这种类型组合的比较。它不是 NotImplementedError 异常。

相等规则应保持稳定、清晰。定义 __eq__ 后,在没有额外设置 __hash__ 的常见情况下,实例会变为不可哈希,不能直接放入集合或作为字典键。不要随意添加哈希逻辑,尤其是在参与比较的字段可以变化时。

10. dataclass:简化数据类

10.1 自动生成常见方法

如果一个类主要用于保存数据,可以使用 dataclass:

python 复制代码
from dataclasses import dataclass


@dataclass
class ProductInfo:
    product_id: int
    name: str
    stock: int = 0


if __name__ == '__main__':
    first = ProductInfo(1001, "矿泉水", 100)
    second = ProductInfo(1001, "矿泉水", 100)

    print(first)
    print(first == second)

运行结果:

复制代码
ProductInfo(product_id=1001, name='矿泉水', stock=100)
True

在默认设置下,dataclass 会根据字段生成常用方法,包括:

  • __init__。
  • __repr__。
  • __eq__。

默认相等比较要求类型相同,再按字段比较,不等同于前面"只比较商品编号"的自定义规则。

10.2 类型注解不等于运行时校验

复制代码
ProductInfo(1001, "矿泉水", "很多")

不会仅因为 stock: int 就自动报错。

如果需要初始化校验,可以使用 __post_init__():

python 复制代码
from dataclasses import dataclass


@dataclass
class ProductInfo:
    product_id: int
    name: str
    stock: int = 0

    def __post_init__(self):
        if type(self.stock) is not int or self.stock < 0:
            raise ValueError("库存必须是非负整数")

if __name__ == '__main__':
    try:
        ProductInfo(1001, "矿泉水", -1)
    except ValueError as error:
        print(error)

运行结果:

复制代码
库存必须是非负整数

注意,这只校验初始化过程。之后直接执行:

复制代码
product.stock = -1

不会自动再次运行 __post_init__。需要持续控制属性变化时,应考虑 property、明确的方法接口或其他设计。

10.3 可变默认值使用 default_factory

python 复制代码
from dataclasses import dataclass, field


@dataclass
class ShoppingCart:
    items: list[str] = field(default_factory=list)

if __name__ == '__main__':
    first = ShoppingCart()
    second = ShoppingCart()
    first.items.append("矿泉水")
    print(first.items)
    print(second.items)

运行结果:

复制代码
['矿泉水']
[]

default_factory=list 会在需要默认值时,为每个实例创建一个新的列表

10.4 frozen=True

如果希望禁止常规字段赋值,可以使用:

复制代码
from dataclasses import dataclass

@dataclass(frozen=True)
class Point:
    x: int
    y: int
if __name__ == '__main__':
    point = Point(10, 20)
    print(point)

运行结果:

复制代码
Point(x=10, y=20)

之后直接执行:

复制代码
point.x = 30

会抛出 FrozenInstanceError。但它不是递归冻结。如果字段引用了一个列表,该列表内部仍然可能被修改。因此,不要把 frozen=True 理解为所有嵌套数据都不可变。

相关推荐
FanetheDivine1 小时前
学习python 2.语法速览
python
奕鼎竜瑆1 小时前
[新手小白也能学会] 01-PyTorch框架使用(上)
人工智能·pytorch·python
海天一色y1 小时前
图像分割全解析:从经典算法到深度学习的原理与实战(Python + MATLAB)
python·深度学习·算法
三点水-here1 小时前
从 Miniconda 迁移到 Miniforge3 全记录:安装、配置、环境迁移与排错实录
python·conda
夜雪一千2 小时前
Python URL解码详解:单层、双重URL解码实战与踩坑指南
linux·服务器·python
VIP_CQCRE2 小时前
用 Ace Data Cloud 一站式接入 AI 视频生成:HappyHorse Videos API 实战指南
python·aigc·api·ai视频·acedatacloud
kali-Myon2 小时前
定向 FGSM 攻击:让神经网络《指猫为狗》
图像处理·python·神经网络·安全·机器学习·fgsm
老歌老听老掉牙2 小时前
旅行商问题:从数学模型到 Python 实战
开发语言·python
CC数分2 小时前
数据可视化入门:用Python把一堆数字变成一张图
开发语言·python·信息可视化·数据分析