from dataclasses import make_dataclass, dataclass, field# =========================================================fields = [ ('name',str,field(default='Unknown')), ('age',int,field(default=0)), ('city',str,field(default='Unknown')),]namespace ={'greet':lambdaself: f"Hello, I'm {self.name} from {self.city}!"}Person =make_dataclass('Person', fields, namespace=namespace)p =Person('John', 30, 'New York')print(p.greet())# Output: Hello, I'm John from New York!p =Person()print(p.greet())# Output: Hello, I'm Unknown from Unknown!# =========================================================# Use @dataclass@dataclassclassPerson: name:str=field(default='Unknown') age:int=field(default=0) city:str=field(default='Unknown')defgreet(self):returnf"Hello, I'm {self.name} from {self.city}!"p =Person('John', 30, 'New York')print(p.greet())# Output: Hello, I'm John from New York!p =Person()print(p.greet())# Output: Hello, I'm Unknown from Unknown!
範例 – bases, namespace (參數)
PYTHON
from dataclasses import dataclass, make_dataclass, field# =========================================================# Define a base classclassMammal:defbreathe(self):return"I can breathe!"# Define the fields for the new dataclassfields = [('name',str,field(default='Unknown'))]# Define additional methodsnamespace ={'greet':lambdaself: f"Hello, I'm {self.name}!",}# Create a new dataclass that inherits from MammalPerson =make_dataclass('Person', fields, bases=(Mammal,), namespace=namespace)p =Person('John')print(p.greet())# Output: Hello, I'm John!print(p.breathe())# Output: I can breathe!# =========================================================# Use @dataclassclassMammal:defbreathe(self):return"I can breathe!"@dataclassclassPerson(Mammal): name:str=field(default='Unknown')defgreet(self):returnf"Hello, I'm {self.name}!"p =Person('John')print(p.greet())# Output: Hello, I'm John!print(p.breathe())# Output: I can breathe!
from dataclasses import dataclass, field, replace@dataclassclassProduct: name:str category:str="General"# This field has a default value and uses the init constructorid:int=field(default=None, init=False)# This field is not included in the init constructordef__post_init__(self):if self.id isNone:# Initialize the id if not already set self.id = self.generate_id()defgenerate_id(self):# In a real scenario, this would generate a unique ID.returnhash((self.name, self.category))product =Product("Table")print(product)# Output: Product(name='Table', category='General', id=some_hash)new_product =replace(product, name="Chair")print(new_product)# Output: Product(name='Chair', category='General', id=different_hash)
在這個例子中:
id 是一個 init=False field。實例化後,呼叫 __post_init__,它設定 id。
當呼叫 replace(product, name="Chair") 時,會有效地建立一個新的 Product 實例,並將 name 設定為 Chair。 id 不是從 product 複製的,而是再次呼叫 __post_init__,根據新的 name 產生新的 id。
如果您想要不同的行為,例如明確複製 init=False 字段,您可以提供自訂替換方法。自訂 replace 方法可確保 init=False field (本例中為 id) 被轉移到新實例,除非它被明確變更。
PYTHON
from dataclasses import dataclass, field, replace@dataclassclassProduct: name:str category:str="General"# This field has a default value and uses the init constructorid:int=field(default=None, init=False)# This field is not included in the init constructordef__post_init__(self):if self.id isNone:# Initialize the id if not already set self.id = self.generate_id()defgenerate_id(self):# In a real scenario, this would generate a unique ID.returnhash((self.name, self.category))defcustom_replace(self,**changes):# Manually create a copy of the object new_obj =Product(self.name, self.category) new_obj.id = self.id # Explicitly copy the `init=False` field# Apply any requested changesfor attr, value in changes.items():setattr(new_obj, attr, value)# Manually call __post_init__ if necessary new_obj.__post_init__()return new_objproduct =Product("Table")print(product)# Output: Product(name='Table', category='General', id=some_hash)new_product = product.custom_replace(name="Chair")print(new_product)# Output: Product(name='Chair', category='General', id=same_hash_as_product)
from dataclasses import dataclass, is_dataclass@dataclassclassPerson: name:str age:intdefis_dataclass_instance(obj):returnis_dataclass(obj)andnotisinstance(obj, type)p =Person('John Doe', 30)# Checking if the class is a dataclass or an instance of a dataclassprint(is_dataclass(Person))# Output: Trueprint(is_dataclass(p))# Output: Trueprint(is_dataclass_instance(Person))# Output: Falseprint(is_dataclass_instance(p))# Output: True
__post_init__
此方法在調用 data class 的 __init__ 方法後立即調用,為初始化後處理提供方便的 hook。
透過使用 __post_init__,您可以確保 data class 實例,在應用程式的其餘部分使用之前,始終處於有效狀態,從而使其成為強大且自我驗證的資料模型的強大功能。
PYTHON
from dataclasses import dataclass, fieldfrom typing import List, Any@dataclassclassInventoryItem:"""Class for keeping track of an item in inventory.""" name:str unit_price:float quantity_on_hand:int=0 serial_numbers: List[str]=field(default_factory=list)def__post_init__(self):if self.unit_price <0:raiseValueError("Unit price cannot be negative")# Automatically calculate total price and validate self.total_price:float= self.unit_price * self.quantity_on_hand# Perform type checking, raise error if serial_numbers is not a listifnotisinstance(self.serial_numbers, list):raiseTypeError("serial_numbers must be a list of strings")defadd_serial_number(self,serial_number:str): self.serial_numbers.append(serial_number)# Example usagetry: item =InventoryItem("Widget", 10.0, 100) item.add_serial_number("SN001")print(item)exceptValueErroras e:print(e)exceptTypeErroras e:print(e)# Output: InventoryItem(name='Widget', unit_price=10.0, quantity_on_hand=100, serial_numbers=['SN001'])
MISSING, KW_ONLY
MISSINGSentinel value 僅提供 default 和 default_factory 設置預設值。
from dataclasses import dataclass, KW_ONLY@dataclassclassStudent: name:str _: KW_ONLY # Pseudo-field to indicate the start of keyword-only fields age:int=20 course:str='Computer Science'# This is OKstudent1 =Student('John Doe', age=21, course='Mathematics')# This is OK - using defaults for keyword-only argumentsstudent2 =Student('Jane Doe')student3 =Student('John Doe', 21, 'Mathematics')# TypeError: Student.__init__() takes 2 positional arguments but 4 were given