XiWind 西風之劍
HomeTechProContactGitHub
  • About
  • Git
    • Windows Terminal、PowerShell 安裝
    • Git 開始使用
    • Branch 入門
    • 合併多個 Commit , 編輯
    • 額外功能
  • deep learning
    • Dilated Convolution
  • Python
    • GIL 【全域直譯器鎖】
    • PyPy 【JIT 編譯器】
    • Decorator 【修飾器】
      • Class Decorators
  • Python library
    • abc 【抽象 Class】
      • ABC, ABCMeta
      • __abstractmethods__, get_cache_token, update_abstractmethods
    • dataclasses 【數據 Class】
      • make_dataclass(), replace(), is_dataclass(), __post_init__
    • enum 【列舉 Class】
      • Flag, auto(), unique, verify()
      • 範例
    • concurrent.futures 【執行緒、程序】
      • Future, Module Functions
    • queue 【佇列、同步】
      • full(), empty(), qsize(), join(), task_done()
    • functools 【可調用物件】
      • ordering、wrapper、partial
      • Overloading
    • heapq 【堆積佇列】
      • heapify(), merge(), nlargest(), nsmallest()
    • time 【時間】
      • time(), monotonic(), perf_counter()...
      • sleep(), 範例...
    • logging 【日誌】
Powered by GitBook
On this page
  • 簡介
  • Field
  • @dataclass
  • 範例 – unsafe_hash (參數)
  • field()
  • 範例 – 基礎
  • 範例 – Mutable 物件
  • fields()
  • asdict()
  • astuple()

Was this helpful?

  1. Python library

dataclasses 【數據 Class】

Previous__abstractmethods__, get_cache_token, update_abstractmethodsNextmake_dataclass(), replace(), is_dataclass(), __post_init__

Last updated 1 year ago

Was this helpful?

簡介


dataclasses module 用於向用戶定義的 Class 新增 special methods (特殊方法),例如:__init__ 或 __repr__。

使用 Dataclasses 的主要優點是在建立 Class 時減少 ,特別是那些主要儲存數據,但沒有太多行為 (方法) 的 Class。

Dataclasses 自動向 Class 加入 special methods,包括:__init__、__repr__ 和 __eq__。 這些 special methods 手動編寫通常很乏味,特別是對於具有許多屬性的 Class。如果需要,也可以自定義和微調這些方法的行為。

數據類的裝飾器是 @dataclasses.dataclass。 這是一個簡單的例子:

PYTHON
import dataclasses

@dataclasses.dataclass
class Point:
    x: int
    y: int

# Create instances of Point (__init__)
p1 = Point(2, 3)
p2 = Point(2, 3)
p3 = Point(3, 4)

# Demonstrates __repr__
print(p1)        # Output: Point(x=2, y=3)

# Demonstrates __eq__
print(p1 == p2)  # Output: True
print(p1 == p3)  # Output: False

Class Point 有兩個 fields x 和 y,這 2 個 fields 都應該是 int。

@dataclass 自動加入一個以 x 和 y 作為參數的 __init__ 方法,一個返回物件的字串表示形式的 __repr__ 方法,以及一個 __eq__ 方法,允許根據 Point 的兩個實例的 x 屬性和 y 屬性來比較它們的相等性。

Field


在 Python Dataclasses 的 context 中,field 是綁定到 Class 的變數,並且該 class 的每個實例都將具有其自己的單獨的 field 副本,來存儲特定於實例的狀態。

在傳統的物件導向術語中,這些通常被稱為 "attributes" or "properties"。Dataclasses 中的 fields 直接在 Class 中定義,並且可以指定它們的類型和預設值。

PYTHON
import dataclasses

@dataclasses.dataclass
class Person:
    name: str
    age: int = 0

在此範例中,name 和 age 是 Person class 的 fields。

name field 應為 str,age field 應為 int,預設值為 0。每個 Person 實例都有自己的 name 和 age fields。

@dataclass


@dataclass 用於生成的 special methods,然後新增到 Class 中。

當 @dataclass 應用於 Class 時,它會掃描該 Class 的 fields。 在這種情況下,fields 是包含類型註釋的 Class-level 變數。

PYTHON
@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)
參數
說明

init

是否將生成 __init__ 方法。 如果 Class 已經定義了 __init__,則忽略此參數。

repr

是否將生成 __repr__ 方法。 生成的 repr String 將包含 Class 名稱以及每個 field 的名稱,按照它們在 Class 中定義的順序排列。 不包括標記為從 repr 中排除的 fields 。如果 Class 已經定義了 __repr__,則忽略此參數。

eq

是否將生成 __eq__ 方法。 此方法按順序比較 Class,僅當它們對應的 fields 相等時,該 Class 的兩個實例才會被比較為相等。如果 Class 已經定義了 __eq__ ,則忽略此參數。

order

是否將生成 __lt__ (小於)、__le__ (小於或等於)、__gt__ (大於) 和 __ge__ (大於或等於) 方法。 這些使得 Class 的實例可排序且具有可比性。如果 field's type 不支援比較,這將引發 TypeError。如果 Class 已經定義了 __lt__、__le__、__gt__ 和 __ge__ 中的任何一個,則會引發 TypeError。

unsafe_hash

如果為 True, 會強制生成 __hash__ 方法,但不建議這樣做,因為它違反了 Python's object model,其中 mutable 物件是 unhashable。 如果為 False,則根據 eq 和 frozen 的設置方式生成 __hash__ 方法。

frozen

在實例建立後,指派 fields 將會引發 Exception。 類似於其他語言中的唯讀 (凍結;frozen) 數據類型,就像 Tuple 一樣。如果 Class 中定義了 __setattr__ 或 __delattr__,則會引發 TypeError。

match_args

kw_only

slots

weakref_slot

生成的方法按照 fields 在 Class 中定義的順序使用 fields。

例如: __init__ 方法中的參數順序遵循 Class 中 field 定義的順序。

以下是管理 __hash__ 方法隱式建立的規則。

如果 frozen=True, eq=True,則將生成 __hash__ 方法。

如果 frozen=False, eq=True,則 __hash__ 方法將被設置為 None,將其標記為 unhashable。

如果 frozen=False, eq=False,則 __hash__ 將保持不變,這意味著將使用 Superclass 的 __hash__ 方法 (如果 Superclass 是 object,將回退到 id-based hashing)。

默認情況下,dataclass() 不會隱式生成 __hash__ 方法 (無法對實例使用 hash())。 它也不會更改現有的顯式定義的 __hash__ 方法。

您不能在 Class 中同時擁有顯式 __hash__ 方法並設置 unsafe_hash=True;這將導致 TypeError。

範例 – unsafe_hash (參數)


PYTHON
import dataclasses

@dataclasses.dataclass(unsafe_hash=True)
class Person:
    name: str
    age: int
    address: str

p = Person("John", 25, "123 Oak St")
print(p)       # Output: Person(name='John', age=25, address='123 Oak St')
print(hash(p)) # -537977084494215504

# =========================================================
@dataclasses.dataclass(unsafe_hash=True)
class AdvancedExample:
    name: str
    age: int
    mutable_field: dict  # mutable field which normally prevents __hash__()

adv_ex = AdvancedExample("John", 25, {"address": "123 Oak St", "phone": "123-456-7890"})

print(f"Hash of the object: {hash(adv_ex)}")
# Output: TypeError: unhashable type: 'dict'

field()


該函數用於自定義 Data class 中的各個 fields。就像指定了預設值本身一樣。

PYTHON
field(*, 
      default=MISSING, 
      default_factory=MISSING, 
      init=True, 
      repr=True, 
      hash=None, 
      compare=True, 
      metadata=None, 
      kw_only=MISSING)
參數
說明

default

Field 的預設值。

default_factory

為 field 提供預設值的零參數函數,當該 field 需要預設值時將調用。 如果同時指定了 default 和 default_factory,則會引發 TypeError。

init

Field 將作為參數包含在生成的 __init__ 方法中。

repr

Field 將包含在生成的 __repr__ 方法,返回的 String 中。

hash

如果為 True,則該 field 包含在生成的 __hash__ 方法中。 如果為 None (預設值),則使用 compare

compare

Field 將包含在生成的 __eq__, __gt__... 方法中。

metadata

kw_only

使用此標記是因為 None 對於某些具有不同含義的參數來說是有效值。 任何程式碼都不應直接使用 MISSING。

範例 – 基礎


Data class Employee 有兩個 fields name 和 age,這兩個 fields 都具有使用 dataclasses.field() 指定的預設值。

PYTHON
from dataclasses import dataclass, field

@dataclass
class Employee:
    name: str = field(default='John Doe')
    age: int = field(default=25)

employee = Employee()
print(employee)  # Output: Employee(name='John Doe', age=25)

default_factory 參數: 當您想要生成每次都應該是新物件的預設值時,default_factory 非常有用。(例如:空 List 或當前時間戳)

PYTHON
from datetime import datetime
from dataclasses import dataclass, field

@dataclass
class LogMessage:
    timestamp: datetime = field(default_factory=datetime.now)
    message: str = field(default="")

log1 = LogMessage()
print(log1)  
# Output: LogMessage(timestamp=datetime.datetime(2023, 7, 21, 2, 18, 12, 994307), message='')

log2 = LogMessage(message="Hello, World!")
print(log2)  
# Output: LogMessage(timestamp=datetime.datetime(2023, 7, 21, 2, 18, 12, 994307), message='Hello, World!')

repr 參數: color field 從 __init__, __repr__ 方法中排除。因此在建立 Point 實例時不需要提供它,在打印 Point 物件時不包括它。

PYTHON
from dataclasses import dataclass, field

@dataclass
class Point:
    x: float
    y: float
    color: str = field(init=False, repr=False, default='White')

p = Point(3.6, 4.8)
print(p)  # Output: Point(x=3.6, y=4.8)

範例 – Mutable 物件


如果 field 是 mutable (例如: List, Dictionary),並且您為其提供預設值,則應該使用 default_factory 來提供一個函數,該函數在每次創建新實例時生成新值。 這避免了在多個實例之間共享相同的可變的值。

PYTHON
import dataclasses

# Correct usage: default_factory for mutable default value
@dataclasses.dataclass
class Right:
    items: list = dataclasses.field(default_factory=list)

r1 = Right()
r2 = Right()
r1.items.append(1)
print(r1.items)  # Outputs: [1]
print(r2.items)  # Outputs: []

# =========================================================
# No default value
@dataclasses.dataclass
class Right:
    items: list

r1 = Right([])
r2 = Right([])
r1.items.append(1)
print(r1.items)  # Outputs: [1]
print(r2.items)  # Outputs: []

# =========================================================
# Incorrect usage: mutable default value
@dataclasses.dataclass
class Wrong:
    items: list = []
# ValueError: mutable default <class 'list'> for field items is not allowed: use default_factory

fields()


fields(class_or_instance) 獲取為特定 Dataclasses 或 Dataclass 實例定義的每個 Field 物件的 metadata。

透過使用 fields(),我們可以對程式碼中的 Dataclasses 進行更詳細的控制和理解。 這對於大量使用 Dataclasses 的複雜程序或 libraries 特別有用。

雖然 fields() 函數提供有關 Dataclass 的有用訊息,但您應該謹慎使用它們,因為訪問 metadata 可能會減慢您的程序速度。 大多數時候,您只需要直接訪問 fields 的值。

PYTHON
from dataclasses import dataclass, fields

@dataclass
class Point:
    x: int
    y: int

print(fields(Point))

執行結果:

(Field(name='x',
       type=<class 'int'>,
       default=<dataclasses._MISSING_TYPE object at 0x000002D0DF83BE50>,
       default_factory=<dataclasses._MISSING_TYPE object at 0x000002D0DF83BE50>,
       init=True,
       repr=True,
       hash=None,
       compare=True,
       metadata=mappingproxy({}),
       kw_only=False,
       _field_type=_FIELD),
 Field(name='y',
       type=<class 'int'>,
       default=<dataclasses._MISSING_TYPE object at 0x000002D0DF83BE50>,
       default_factory=<dataclasses._MISSING_TYPE object at 0x000002D0DF83BE50>,
       init=True,
       repr=True,
       hash=None,
       compare=True,
       metadata=mappingproxy({}),
       kw_only=False,
       _field_type=_FIELD))

範例 – 基礎


PYTHON
import dataclasses

@dataclasses.dataclass
class ComplexClass:
    name: str
    value: int = dataclasses.field(default=42, repr=False)
    list_values: list = dataclasses.field(default_factory=list)

# Create an instance
complex_instance = ComplexClass('example')

# Retrieve and print fields information
for f in dataclasses.fields(ComplexClass):
    print(f'Name: {f.name}, Type: {f.type}, Default: {f.default if f.default is not dataclasses.MISSING else "No default"}, Repr: {f.repr}')

# You can also pass an instance of the class
for f in dataclasses.fields(complex_instance):
    print(f'Name: {f.name}, Type: {f.type}, Default: {f.default if f.default is not dataclasses.MISSING else "No default"}, Repr: {f.repr}')

執行結果:

Name: name, Type: <class 'str'>, Default: No default, Repr: True
Name: value, Type: <class 'int'>, Default: 42, Repr: False
Name: list_values, Type: <class 'list'>, Default: No default, Repr: True

Name: name, Type: <class 'str'>, Default: No default, Repr: True
Name: value, Type: <class 'int'>, Default: 42, Repr: False
Name: list_values, Type: <class 'list'>, Default: No default, Repr: True

asdict()


asdict(obj, *, dict_factory=dict) 每個 Dataclass fields 都轉換為 Dictionary,作為 name: value pairs。

透過 dict_factory,將 Dataclass obj 轉換為 dict。(deep copy)

PYTHON
from dataclasses import dataclass, asdict

@dataclass
class Point:
    x: int
    y: int

p = Point(10, 20)
print(asdict(p))  # Outputs: {'x': 10, 'y': 20}

範例 – 具有複雜 field 類型的 Dataclass


PYTHON
from dataclasses import dataclass, asdict

@dataclass
class Address:
    city: str
    country: str

@dataclass
class Person:
    name: str
    address: Address

person = Person('John', Address('NYC', 'USA'))
print(asdict(person))

執行結果:

Output: {'name': 'John', 
         'address': {'city': 'NYC', 'country': 'USA'}}

collections.OrderedDict 保證 fields 的顯示順序與 Dataclass 中定義的順序相同。

from pprint import pprint
from collections import OrderedDict
from dataclasses import dataclass, asdict, field

@dataclass
class Employee:
    name: str
    age: int
    skills: list = field(default_factory=list)

@dataclass
class Company:
    name: str
    employees: list = field(default_factory=list)

company = Company('Example Corp.', 
                  [Employee('John', 30, ['Python', 'Java']), 
                   Employee('Jane', 28, ['Go', 'Rust'])])

pprint(asdict(company))
pprint(asdict(company, dict_factory=OrderedDict))  

執行結果:

{'employees': [{'age': 30, 'name': 'John', 'skills': ['Python', 'Java']},
               {'age': 28, 'name': 'Jane', 'skills': ['Go', 'Rust']}],
 'name': 'Example Corp.'}

OrderedDict([('name', 'Example Corp.'),
             ('employees',
              [OrderedDict([('name', 'John'),
                            ('age', 30),
                            ('skills', ['Python', 'Java'])]),
               OrderedDict([('name', 'Jane'),
                            ('age', 28),
                            ('skills', ['Go', 'Rust'])])])])

範例 – shallow_copy(obj)


asdict() 方法創建一個 deep copy,它比 shallow copy 的計算量更大,特別是對於大型 Dataclasses。

如果您知道不需要修改 Dataclass 中的任何物件,並且只需要它的簡單 dictionary 表示形式,則 shallow_copy(obj) 可能會更有效。

或者,如果您想要維護 dataclass 實例,您可能需要使用 shallow_copy(obj)。

PYTHON
from dataclasses import dataclass, asdict, fields

# =========================================================
# Deep copy

@dataclass
class Employee:
    name: str
    skills: list

emp = Employee('John', ['Python', 'Java'])
emp_dict = asdict(emp)

# Add a skill to the List
emp.skills.append('Go')
print(emp.skills)          # Output: ['Python', 'Java', 'Go']
print(emp_dict['skills'])  # Output: ['Python', 'Java']

# =========================================================
# Shallow copy

@dataclass
class Example:
    name: str
    skills: list

def shallow_copy(obj):
    return dict((field.name, getattr(obj, field.name)) for field in fields(obj))

ex = Example('John', ['Python', 'Java'])
copied_ex = shallow_copy(ex)

# Add a skill to the List
ex.skills.append('Go')
print(ex.skills)            # Output: ['Python', 'Java', 'Go']
print(copied_ex['skills'])  # Output: ['Python', 'Java', 'Go']

astuple()


astuple(obj, *, tuple_factory=tuple) 每個 Dataclass fields 都轉換為 Tuple。透過 dict_factory,將 Dataclass obj 轉換為 tuple。

PYTHON
from dataclasses import dataclass, astuple, fields

@dataclass
class Point:
    x: int
    y: int

@dataclass
class Line:
    start: Point
    end: Point

# Shallow copy
def shallow_copy(obj):  
    return tuple(getattr(obj, field.name) for field in fields(obj))

line = Line(Point(0, 0), Point(10, 20))
print(astuple(line))  # Output: ((0, 0), (10, 20))

將值包裝在 中,以使其唯讀,並在 field 物件上公開。 它主要用於第三方擴展,Data Classes 本身不使用。

MISSING 是一個 物件,用於檢測使用者是否提供了某些參數。

Boilerplate code
Sentinel value
MappingProxyType()
Page cover image