dataclasses --- 數(shù)據(jù)類?

源碼: Lib/dataclasses.py


這個(gè)模塊提供了一個(gè)裝飾器和一些函數(shù),用于自動(dòng)添加生成的 special method,例如 __init__()__repr__() 到用戶定義的類。 它最初描述于 PEP 557

在這些生成的方法中使用的成員變量是使用 PEP 526 類型標(biāo)注來定義的。 例如以下代碼:

from dataclasses import dataclass

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

將在添加的內(nèi)容中包括如下所示的 __init__():

def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):
    self.name = name
    self.unit_price = unit_price
    self.quantity_on_hand = quantity_on_hand

請(qǐng)注意,此方法會(huì)自動(dòng)添加到類中:它不會(huì)在上面顯示的 InventoryItem 定義中直接指定。

3.7 新版功能.

模塊內(nèi)容?

@dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False)?

這個(gè)函數(shù)是 decorator ,用于將生成的 special method 添加到類中,如下所述。

dataclass() 裝飾器會(huì)檢查類以查找 fieldfield 被定義為具有 類型標(biāo)注 的類變量。 除了下面描述的兩個(gè)例外,在 dataclass() 中沒有什么東西會(huì)去檢查在變量標(biāo)注中所指定的類型。

所有生成的方法中的字段順序是它們?cè)陬惗x中出現(xiàn)的順序。

dataclass() 裝飾器將向類中添加各種“dunder”方法,如下所述。 如果所添加的方法已存在于類中,則行為將取決于下面所列出的參數(shù)。 裝飾器會(huì)返回調(diào)用它的類本身;不會(huì)創(chuàng)建新的類。

如果 dataclass() 僅用作沒有參數(shù)的簡(jiǎn)單裝飾器,它就像它具有此簽名中記錄的默認(rèn)值一樣。也就是說,這三種 dataclass() 用法是等價(jià)的:

@dataclass
class C:
    ...

@dataclass()
class C:
    ...

@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False)
class C:
   ...

dataclass() 的參數(shù)有:

  • init: 如果為真值(默認(rèn)),將生成一個(gè) __init__() 方法。

    如果類已定義 __init__() ,則忽略此參數(shù)。

  • repr :如果為真值(默認(rèn)),將生成一個(gè) __repr__() 方法。 生成的 repr 字符串將具有類名以及每個(gè)字段的名稱和 repr ,按照它們?cè)陬愔卸x的順序。不包括標(biāo)記為從 repr 中排除的字段。 例如:InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)

    如果類已定義 __repr__() ,則忽略此參數(shù)。

  • eq :如果為true(默認(rèn)值),將生成 __eq__() 方法。此方法將類作為其字段的元組按順序比較。比較中的兩個(gè)實(shí)例必須是相同的類型。

    如果類已定義 __eq__() ,則忽略此參數(shù)。

  • order :如果為真值(默認(rèn)為 False ),則 __lt__() 、 __le__()__gt__()__ge__() 方法將生成。 這將類作為其字段的元組按順序比較。比較中的兩個(gè)實(shí)例必須是相同的類型。如果 order 為真值并且 eq 為假值 ,則引發(fā) ValueError 。

    如果類已經(jīng)定義了 __lt__()__le__() 、 __gt__() 或者 __ge__() 中的任意一個(gè),將引發(fā) TypeError 。

  • unsafe_hash :如果為 False (默認(rèn)值),則根據(jù) eqfrozen 的設(shè)置方式生成 __hash__() 方法。

    __hash__() 由內(nèi)置的 hash() 使用,當(dāng)對(duì)象被添加到散列集合(如字典和集合)時(shí)。有一個(gè) __hash__() 意味著類的實(shí)例是不可變的。可變性是一個(gè)復(fù)雜的屬性,取決于程序員的意圖, __eq__() 的存在性和行為,以及 dataclass() 裝飾器中 eqfrozen 標(biāo)志的值。

    默認(rèn)情況下, dataclass() 不會(huì)隱式添加 __hash__() 方法,除非這樣做是安全的。 它也不會(huì)添加或更改現(xiàn)有的明確定義的 __hash__() 方法。 設(shè)置類屬性 __hash__ = None 對(duì) Python 具有特定含義,如 __hash__() 文檔中所述。

    如果 __hash__() 沒有顯式定義,或者它被設(shè)為 None,則 dataclass() 可能 會(huì)添加一個(gè)隱式 __hash__() 方法。 雖然并不推薦,但你可以用 unsafe_hash=True 來強(qiáng)制 dataclass() 創(chuàng)建一個(gè) __hash__() 方法。 如果你的類在邏輯上不可變但卻仍然可被修改那么可能就是這種情況。 這是一個(gè)特殊用例并且應(yīng)當(dāng)被仔細(xì)地考慮。

    以下是隱式創(chuàng)建 __hash__() 方法的規(guī)則。請(qǐng)注意,你不能在數(shù)據(jù)類中都使用顯式的 __hash__() 方法并設(shè)置 unsafe_hash=True ;這將導(dǎo)致 TypeError 。

    如果 eqfrozen 都是 true,默認(rèn)情況下 dataclass() 將為你生成一個(gè) __hash__() 方法。如果 eq 為 true 且 frozen 為 false ,則 __hash__() 將被設(shè)置為 None ,標(biāo)記它不可用(因?yàn)樗强勺兊模?。如?eq 為 false ,則 __hash__() 將保持不變,這意味著將使用超類的 __hash__() 方法(如果超類是 object ,這意味著它將回到基于id的hash)。

  • frozen: 如為真值 (默認(rèn)值為 False),則對(duì)字段賦值將會(huì)產(chǎn)生異常。 這模擬了只讀的凍結(jié)實(shí)例。 如果在類中定義了 __setattr__()__delattr__() 則將會(huì)引發(fā) TypeError。 參見下文的討論。

  • match_args: 如果為真值 (默認(rèn)值為 True),則將根據(jù)傳給生成的 __init__() 方法的形參列表來創(chuàng)建 __match_args__ 元組 (即使沒有生成 __init__() 也會(huì)創(chuàng)建,見上文)。 如果為假值,或者如果 __match_args__ 已在類中定義,則將不生成 __match_args__

3.10 新版功能.

  • kw_only: If true (the default value is False), then all fields will be marked as keyword-only. If a field is marked as keyword-only, then the only effect is that the __init__() parameter generated from a keyword-only field must be specified with a keyword when __init__() is called. There is no effect on any other aspect of dataclasses. See the parameter glossary entry for details. Also see the KW_ONLY section.

3.10 新版功能.

  • slots: 如果為真值 (默認(rèn)值為 False),則將生成 __slots__ 屬性并將返回一個(gè)新類而非原來的類。 如果 __slots__ 已在類中定義,則會(huì)引發(fā) TypeError。

3.10 新版功能.

在 3.11 版更改: If a field name is already included in the __slots__ of a base class, it will not be included in the generated __slots__ to prevent overriding them. Therefore, do not use __slots__ to retrieve the field names of a dataclass. Use fields() instead. To be able to determine inherited slots, base class __slots__ may be any iterable, but not an iterator.

  • weakref_slot: If true (the default is False), add a slot named "__weakref__", which is required to make an instance weakref-able. It is an error to specify weakref_slot=True without also specifying slots=True.

3.11 新版功能.

fields 可以選擇使用普通的 Python 語法指定默認(rèn)值:

@dataclass
class C:
    a: int       # 'a' has no default value
    b: int = 0   # assign a default value for 'b'

在這個(gè)例子中, ab 都將包含在添加的 __init__() 方法中,它們將被定義為:

def __init__(self, a: int, b: int = 0):

如果具有默認(rèn)值的字段之后存在沒有默認(rèn)值的字段,將會(huì)引發(fā) TypeError。 無論此情況是發(fā)生在單個(gè)類中還是作為類繼承的結(jié)果,都是如此。

dataclasses.field(*, default=MISSING, default_factory=MISSING, init=True, repr=True, hash=None, compare=True, metadata=None, kw_only=MISSING)?

對(duì)于常見和簡(jiǎn)單的用例,不需要其他功能。但是,有些數(shù)據(jù)類功能需要額外的每字段信息。為了滿足這種對(duì)附加信息的需求,你可以通過調(diào)用提供的 field() 函數(shù)來替換默認(rèn)字段值。例如:

@dataclass
class C:
    mylist: list[int] = field(default_factory=list)

c = C()
c.mylist += [1, 2, 3]

As shown above, the MISSING value is a sentinel object used to detect if some parameters are provided by the user. This sentinel is used because None is a valid value for some parameters with a distinct meaning. No code should directly use the MISSING value.

field() 參數(shù)有:

  • default :如果提供,這將是該字段的默認(rèn)值。這是必需的,因?yàn)?field() 調(diào)用本身會(huì)替換一般的默認(rèn)值。

  • default_factory :如果提供,它必須是一個(gè)零參數(shù)可調(diào)用對(duì)象,當(dāng)該字段需要一個(gè)默認(rèn)值時(shí),它將被調(diào)用。除了其他目的之外,這可以用于指定具有可變默認(rèn)值的字段,如下所述。 同時(shí)指定 defaultdefault_factory 將產(chǎn)生錯(cuò)誤。

  • init :如果為true(默認(rèn)值),則該字段作為參數(shù)包含在生成的 __init__() 方法中。

  • repr :如果為true(默認(rèn)值),則該字段包含在生成的 __repr__() 方法返回的字符串中。

  • hash :這可以是布爾值或 None 。如果為true,則此字段包含在生成的 __hash__() 方法中。如果為 None (默認(rèn)值),請(qǐng)使用 compare 的值,這通常是預(yù)期的行為。如果字段用于比較,則應(yīng)在 hash 中考慮該字段。不鼓勵(lì)將此值設(shè)置為 None 以外的任何值。

    設(shè)置 hash=Falsecompare=True 的一個(gè)可能原因是,如果一個(gè)計(jì)算 hash 的代價(jià)很高的字段是檢驗(yàn)等價(jià)性需要的,但還有其他字段可以計(jì)算類型的 hash 。 即使從 hash 中排除某個(gè)字段,它仍將用于比較。

  • compare :如果為true(默認(rèn)值),則該字段包含在生成的相等性和比較方法中( __eq__() , __gt__() 等等)。

  • metadata :這可以是映射或 None 。 None 被視為一個(gè)空的字典。這個(gè)值包含在 MappingProxyType() 中,使其成為只讀,并暴露在 Field 對(duì)象上。數(shù)據(jù)類根本不使用它,它是作為第三方擴(kuò)展機(jī)制提供的。多個(gè)第三方可以各自擁有自己的鍵值,以用作元數(shù)據(jù)中的命名空間。

  • kw_only: 如果為真值,則此字段將被標(biāo)記為僅限關(guān)鍵字。 這將在當(dāng)計(jì)算出所生成的 __init__() 方法的形參時(shí)被使用。

3.10 新版功能.

如果通過調(diào)用 field() 指定字段的默認(rèn)值,則該字段的類屬性將替換為指定的 default 值。如果沒有提供 default ,那么將刪除類屬性。目的是在 dataclass() 裝飾器運(yùn)行之后,類屬性將包含字段的默認(rèn)值,就像指定了默認(rèn)值一樣。例如,之后:

@dataclass
class C:
    x: int
    y: int = field(repr=False)
    z: int = field(repr=False, default=10)
    t: int = 20

類屬性 C.z 將是 10 ,類屬性 C.t 將是 20,類屬性 C.xC.y 將不設(shè)置。

class dataclasses.Field?

Field 對(duì)象描述每個(gè)定義的字段。這些對(duì)象在內(nèi)部創(chuàng)建,并由 fields() 模塊級(jí)方法返回(見下文)。用戶永遠(yuǎn)不應(yīng)該直接實(shí)例化 Field 對(duì)象。 其有文檔的屬性是:

  • name :字段的名字。

  • type :字段的類型。

  • default, default_factory, init, repr, hash, compare, metadatakw_only 具有與 field() 函數(shù)中對(duì)應(yīng)參數(shù)相同的含義和值。

可能存在其他屬性,但它們是私有的,不能被審查或依賴。

dataclasses.fields(class_or_instance)?

返回 Field 對(duì)象的元組,用于定義此數(shù)據(jù)類的字段。 接受數(shù)據(jù)類或數(shù)據(jù)類的實(shí)例。如果沒有傳遞一個(gè)數(shù)據(jù)類或?qū)嵗龑⒁l(fā) TypeError 。 不返回 ClassVarInitVar 的偽字段。

dataclasses.asdict(obj, *, dict_factory=dict)?

Converts the dataclass obj to a dict (by using the factory function dict_factory). Each dataclass is converted to a dict of its fields, as name: value pairs. dataclasses, dicts, lists, and tuples are recursed into. Other objects are copied with copy.deepcopy().

Example of using asdict() on nested dataclasses:

@dataclass
class Point:
     x: int
     y: int

@dataclass
class C:
     mylist: list[Point]

p = Point(10, 20)
assert asdict(p) == {'x': 10, 'y': 20}

c = C([Point(0, 0), Point(10, 4)])
assert asdict(c) == {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}

To create a shallow copy, the following workaround may be used:

dict((field.name, getattr(obj, field.name)) for field in fields(obj))

asdict() raises TypeError if obj is not a dataclass instance.

dataclasses.astuple(obj, *, tuple_factory=tuple)?

Converts the dataclass obj to a tuple (by using the factory function tuple_factory). Each dataclass is converted to a tuple of its field values. dataclasses, dicts, lists, and tuples are recursed into. Other objects are copied with copy.deepcopy().

繼續(xù)前一個(gè)例子:

assert astuple(p) == (10, 20)
assert astuple(c) == ([(0, 0), (10, 4)],)

To create a shallow copy, the following workaround may be used:

tuple(getattr(obj, field.name) for field in dataclasses.fields(obj))

astuple() raises TypeError if obj is not a dataclass instance.

dataclasses.make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False)?

Creates a new dataclass with name cls_name, fields as defined in fields, base classes as given in bases, and initialized with a namespace as given in namespace. fields is an iterable whose elements are each either name, (name, type), or (name, type, Field). If just name is supplied, typing.Any is used for type. The values of init, repr, eq, order, unsafe_hash, frozen, match_args, kw_only, slots, and weakref_slot have the same meaning as they do in dataclass().

此函數(shù)不是嚴(yán)格要求的,因?yàn)橛糜谌魏蝿?chuàng)建帶有 __annotations__ 的新類的 Python 機(jī)制都可以應(yīng)用 dataclass() 函數(shù)將該類轉(zhuǎn)換為數(shù)據(jù)類。提供此功能是為了方便。例如:

C = make_dataclass('C',
                   [('x', int),
                     'y',
                    ('z', int, field(default=5))],
                   namespace={'add_one': lambda self: self.x + 1})

等價(jià)于

@dataclass
class C:
    x: int
    y: 'typing.Any'
    z: int = 5

    def add_one(self):
        return self.x + 1
dataclasses.replace(obj, /, **changes)?

Creates a new object of the same type as obj, replacing fields with values from changes. If obj is not a Data Class, raises TypeError. If values in changes do not specify fields, raises TypeError.

新返回的對(duì)象通過調(diào)用數(shù)據(jù)類的 __init__() 方法創(chuàng)建。這確保了如果存在 __post_init__() ,其也被調(diào)用。

如果存在沒有默認(rèn)值的僅初始化變量,必須在調(diào)用 replace() 時(shí)指定,以便它們可以傳遞給 __init__()__post_init__() 。

changes 包含任何定義為 init=False 的字段是錯(cuò)誤的。在這種情況下會(huì)引發(fā) ValueError 。

提前提醒 init=False 字段在調(diào)用 replace() 時(shí)的工作方式。如果它們完全被初始化的話,它們不是從源對(duì)象復(fù)制的,而是在 __post_init__() 中初始化。估計(jì) init=False 字段很少能被正確地使用。如果使用它們,那么使用備用類構(gòu)造函數(shù)或者可能是處理實(shí)例復(fù)制的自定義 replace() (或類似命名的)方法可能是明智的。

dataclasses.is_dataclass(obj)?

如果其形參為 dataclass 或其實(shí)例則返回 True,否則返回 False。

如果你需要知道一個(gè)類是否是一個(gè)數(shù)據(jù)類的實(shí)例(而不是一個(gè)數(shù)據(jù)類本身),那么再添加一個(gè) not isinstance(obj, type) 檢查:

def is_dataclass_instance(obj):
    return is_dataclass(obj) and not isinstance(obj, type)
dataclasses.MISSING?

一個(gè)表示缺失 default 或 default_factory 的監(jiān)視值。

dataclasses.KW_ONLY?

一個(gè)用作類型標(biāo)注的監(jiān)視值。 任何在偽字段之后的類型為 KW_ONLY 的字段會(huì)被標(biāo)記為僅限關(guān)鍵字字段。 請(qǐng)注意在其他情況下 KW_ONLY 類型的偽字段會(huì)被完全忽略。 這包括此類字段的名稱。 根據(jù)慣例,名稱 _ 會(huì)被用作 KW_ONLY 字段。 僅限關(guān)鍵字字段指明當(dāng)類被實(shí)例化時(shí) __init__() 形參必須以關(guān)鍵字形式來指定。

在這個(gè)例子中,字段 yz 將被標(biāo)記為僅限關(guān)鍵字字段:

@dataclass
class Point:
  x: float
  _: KW_ONLY
  y: float
  z: float

p = Point(0, y=1.5, z=2.0)

在單個(gè)數(shù)據(jù)類中,指定一個(gè)以上 KW_ONLY 類型的字段將導(dǎo)致錯(cuò)誤。

3.10 新版功能.

exception dataclasses.FrozenInstanceError?

在使用 frozen=True 定義的數(shù)據(jù)類上調(diào)用隱式定義的 __setattr__()__delattr__() 時(shí)引發(fā)。 這是 AttributeError 的一個(gè)子類。

初始化后處理?

生成的 __init__() 代碼將調(diào)用一個(gè)名為 __post_init__() 的方法,如果在類上已經(jīng)定義了 __post_init__() 。它通常被稱為 self.__post_init__() 。但是,如果定義了任何 InitVar 字段,它們也將按照它們?cè)陬愔卸x的順序傳遞給 __post_init__() 。 如果沒有 __init__() 方法生成,那么 __post_init__() 將不會(huì)被自動(dòng)調(diào)用。

在其他用途中,這允許初始化依賴于一個(gè)或多個(gè)其他字段的字段值。例如:

@dataclass
class C:
    a: float
    b: float
    c: float = field(init=False)

    def __post_init__(self):
        self.c = self.a + self.b

dataclass() 所生成的 __init__() 方法不會(huì)調(diào)用基類的 __init__() 方法。 如果基類有需要被調(diào)用的 __init__() 方法,通常是在 __post_init__() 方法中調(diào)用此方法:

@dataclass
class Rectangle:
    height: float
    width: float

@dataclass
class Square(Rectangle):
    side: float

    def __post_init__(self):
        super().__init__(self.side, self.side)

但是請(qǐng)注意,一般來說 dataclass 生成的 __init__() 方法不需要被調(diào)用,因?yàn)榕缮?dataclass 將負(fù)責(zé)初始化任何自身為 dataclass 的基類的所有字段。

有關(guān)將參數(shù)傳遞給 __post_init__() 的方法,請(qǐng)參閱下面有關(guān)僅初始化變量的段落。另請(qǐng)參閱關(guān)于 replace() 處理 init=False 字段的警告。

類變量?

兩個(gè)地方 dataclass() 實(shí)際檢查字段類型的之一是確定字段是否是如 PEP 526 所定義的類變量。它通過檢查字段的類型是否為 typing.ClassVar 來完成此操作。如果一個(gè)字段是一個(gè) ClassVar ,它將被排除在考慮范圍之外,并被數(shù)據(jù)類機(jī)制忽略。這樣的 ClassVar 偽字段不會(huì)由模塊級(jí)的 fields() 函數(shù)返回。

僅初始化變量?

另一個(gè) dataclass() 檢查類型注解地方是為了確定一個(gè)字段是否是一個(gè)僅初始化變量。它通過查看字段的類型是否為 dataclasses.InitVar 類型來實(shí)現(xiàn)。如果一個(gè)字段是一個(gè) InitVar ,它被認(rèn)為是一個(gè)稱為僅初始化字段的偽字段。因?yàn)樗皇且粋€(gè)真正的字段,所以它不會(huì)被模塊級(jí)的 fields() 函數(shù)返回。僅初始化字段作為參數(shù)添加到生成的 __init__() 方法中,并傳遞給可選的 __post_init__() 方法。數(shù)據(jù)類不會(huì)使用它們。

例如,假設(shè)一個(gè)字段將從數(shù)據(jù)庫(kù)初始化,如果在創(chuàng)建類時(shí)未提供其值:

@dataclass
class C:
    i: int
    j: int = None
    database: InitVar[DatabaseType] = None

    def __post_init__(self, database):
        if self.j is None and database is not None:
            self.j = database.lookup('j')

c = C(10, database=my_database)

在這種情況下, fields() 將返回 ijField 對(duì)象,但不包括 database 。

凍結(jié)的實(shí)例?

無法創(chuàng)建真正不可變的 Python 對(duì)象。但是,通過將 frozen=True 傳遞給 dataclass() 裝飾器,你可以模擬不變性。在這種情況下,數(shù)據(jù)類將向類添加 __setattr__()__delattr__() 方法。 些方法在調(diào)用時(shí)會(huì)引發(fā) FrozenInstanceError 。

使用 frozen=True 時(shí)會(huì)有很小的性能損失: __ init__() 不能使用簡(jiǎn)單的賦值來初始化字段,并必須使用 object.__setattr__()。

繼承?

當(dāng)數(shù)組由 dataclass() 裝飾器創(chuàng)建時(shí),它會(huì)查看反向 MRO 中的所有類的基類(即從 object 開始 ),并且對(duì)于它找到的每個(gè)數(shù)據(jù)類, 將該基類中的字段添加到字段的有序映射中。添加完所有基類字段后,它會(huì)將自己的字段添加到有序映射中。所有生成的方法都將使用這種組合的,計(jì)算的有序字段映射。由于字段是按插入順序排列的,因此派生類會(huì)重載基類。一個(gè)例子:

@dataclass
class Base:
    x: Any = 15.0
    y: int = 0

@dataclass
class C(Base):
    z: int = 10
    x: int = 15

最后的字段列表依次是 xy 、 z 。 x 的最終類型是 int ,如類 C 中所指定的那樣。

C 生成的 __init__() 方法看起來像:

def __init__(self, x: int = 15, y: int = 0, z: int = 10):

__init__() 中僅限關(guān)鍵字字段的重新排序?

在計(jì)算出 __init__() 所需要的形參之后,任何僅限關(guān)鍵字形參會(huì)被移至所有常規(guī)(非僅限關(guān)鍵字)形參的后面。 這是 Python 中實(shí)現(xiàn)僅限關(guān)鍵字形參所要求的:它們必須位于非僅限關(guān)鍵字形參之后。

在這個(gè)例子中,Base.y, Base.w, and D.t 是僅限關(guān)鍵字字段,而 Base.xD.z 是常規(guī)字段:

@dataclass
class Base:
    x: Any = 15.0
    _: KW_ONLY
    y: int = 0
    w: int = 1

@dataclass
class D(Base):
    z: int = 10
    t: int = field(kw_only=True, default=0)

D 生成的 __init__() 方法看起來像是這樣:

def __init__(self, x: Any = 15.0, z: int = 10, *, y: int = 0, w: int = 1, t: int = 0):

請(qǐng)注意形參原來在字段列表中出現(xiàn)的位置已被重新排序:前面是來自常規(guī)字段的形參而后面是來自僅限關(guān)鍵字字段的形參。

僅限關(guān)鍵字形參的相對(duì)順序會(huì)在重新排序的 __init__() 形參列表中保持原樣。

默認(rèn)工廠函數(shù)?

如果一個(gè) field() 指定了一個(gè) default_factory ,當(dāng)需要該字段的默認(rèn)值時(shí),將使用零參數(shù)調(diào)用它。例如,要?jiǎng)?chuàng)建列表的新實(shí)例,請(qǐng)使用:

mylist: list = field(default_factory=list)

如果一個(gè)字段被排除在 __init__() 之外(使用 init=False )并且字段也指定 default_factory ,則默認(rèn)的工廠函數(shù)將始終從生成的 __init__() 函數(shù)調(diào)用。發(fā)生這種情況是因?yàn)闆]有其他方法可以為字段提供初始值。

可變的默認(rèn)值?

Python 在類屬性中存儲(chǔ)默認(rèn)成員變量值。思考這個(gè)例子,不使用數(shù)據(jù)類:

class C:
    x = []
    def add(self, element):
        self.x.append(element)

o1 = C()
o2 = C()
o1.add(1)
o2.add(2)
assert o1.x == [1, 2]
assert o1.x is o2.x

請(qǐng)注意,類 C 的兩個(gè)實(shí)例共享相同的類變量 x ,如預(yù)期的那樣。

使用數(shù)據(jù)類, 如果 此代碼有效:

@dataclass
class D:
    x: List = []
    def add(self, element):
        self.x += element

它生成的代碼類似于:

class D:
    x = []
    def __init__(self, x=x):
        self.x = x
    def add(self, element):
        self.x += element

assert D().x is D().x

This has the same issue as the original example using class C. That is, two instances of class D that do not specify a value for x when creating a class instance will share the same copy of x. Because dataclasses just use normal Python class creation they also share this behavior. There is no general way for Data Classes to detect this condition. Instead, the dataclass() decorator will raise a TypeError if it detects an unhashable default parameter. The assumption is that if a value is unhashable, it is mutable. This is a partial solution, but it does protect against many common errors.

使用默認(rèn)工廠函數(shù)是一種創(chuàng)建可變類型新實(shí)例的方法,并將其作為字段的默認(rèn)值:

@dataclass
class D:
    x: list = field(default_factory=list)

assert D().x is not D().x

在 3.11 版更改: Instead of looking for and disallowing objects of type list, dict, or set, unhashable objects are now not allowed as default values. Unhashability is used to approximate mutability.