كود تكراري أقل للفئات التي تحمل بيانات
توجد فئات كثيرة أساسًا لتحمل بضعة حقول: صفقة أو عرض سعر أو إعداد. وكتابة __init__ و__repr__ و__eq__ لكل منها مكررة. يكتبها لك المزخرف @dataclass من الوحدة dataclasses انطلاقًا من قائمة حقول موسومة. تعلن symbol: str وprice: float وquantity: int مرة واحدة فتحصل على دالة بانية Trade("AAPL", 184.25, 200) وعلى repr مقروء مثل Trade(symbol='AAPL', price=184.25, quantity=200) وتساوٍ حقلًا بحقل. والتوسيمات توثّق القصد؛ ولا تفرضها بايثون.
from dataclasses import dataclass
@dataclass
class Trade:
symbol: str
price: float
quantity: int
a = Trade("AAPL", 184.25, 200)
b = Trade("AAPL", 184.25, 200)
print(a) # Trade(symbol='AAPL', price=184.25, quantity=200)
print(a == b) # True: compared field by field
print(a.price * a.quantity) # 36850.0القيم الافتراضية و field() والقيم القابلة للتعديل
يمكن أن يكون للحقل قيمة افتراضية (currency: str = "USD")، ويجب أن تأتي الحقول ذات القيم الافتراضية بعد الحقول التي بلا قيم. أما لقيمة افتراضية قابلة للتعديل كقائمة فسيتشاركها كل كائن إذا كتبت = [] (وترفضها بايثون)؛ اكتب trades: list = field(default_factory=list) ليحصل كل كائن على قائمته الجديدة. ويتيح field(...) أيضًا إخفاء حقل من repr (repr=False) أو من المقارنات (compare=False).
from dataclasses import dataclass, field
@dataclass
class Book:
name: str
currency: str = "USD" # a default
trades: list = field(default_factory=list) # a fresh list per object
one = Book("desk-a")
two = Book("desk-b")
one.trades.append(("AAPL", 100))
print(one) # Book(name='desk-a', currency='USD', trades=[('AAPL', 100)])
print(two.trades) # [] (not shared)
print(Book("x", "EUR")) # Book(name='x', currency='EUR', trades=[])دوالك الخاصة و __post_init__ ودوال بانية بديلة
تبقى الفئة البيانية فئة عادية فيمكنك إضافة دوال إليها. يعمل __post_init__(self) مباشرة بعد __init__ المولَّد، وهو موضع التحقق من القيم أو تنظيفها (self.symbol = self.symbol.upper() وraise ValueError(...) لسعر سيئ). ويستقبل @classmethod الفئة باسم cls وهو الطريقة المعتادة لبناء كائن من شكل آخر كالنص: Trade.from_line("AAPL,184.25,200") تُرجع cls(symbol, float(price), int(quantity)) فيبقى التحقق يعمل وتعمل الفئات الفرعية أيضًا.
from dataclasses import dataclass
@dataclass
class Trade:
symbol: str
price: float
quantity: int
def __post_init__(self):
self.symbol = self.symbol.strip().upper()
if self.price <= 0:
raise ValueError("price must be positive")
def notional(self):
return self.price * abs(self.quantity)
@classmethod
def from_line(cls, line):
symbol, price, quantity = line.split(",")
return cls(symbol, float(price), int(quantity))
t = Trade.from_line(" aapl,184.25,-200")
print(t) # Trade(symbol='AAPL', price=184.25, quantity=-200)
print(t.notional()) # 36850.0مجمّدة ومرتّبة ومتى تتفوق الفئات العادية
يجعل @dataclass(frozen=True) النسخ للقراءة فقط (الإسناد إلى حقل يرفع FrozenInstanceError) ويمنحها تجزئة فتصلح مفاتيح قاموس أو أعضاء مجموعة: مثالي للقيم التي لا ينبغي أن تتغير كمواصفات العقد. ويضيف @dataclass(order=True) المعاملات < و<= و> و>= بمقارنة الحقول بالترتيب. استخدم الفئة البيانية لسجلات البيانات؛ أما الكائنات الغنية بالسلوك والحالة الخفية فالفئة المكتوبة يدويًا بدوال مدروسة أوضح.
from dataclasses import dataclass
@dataclass(frozen=True, order=True)
class Contract:
symbol: str
expiry: str # ISO dates sort correctly as text
a = Contract("ESZ5", "2025-12-19")
b = Contract("ESZ5", "2026-03-20")
print(a < b) # True
print({a: "front", b: "next"}[a]) # front (frozen => hashable)
try:
a.symbol = "NQZ5"
except Exception as error:
print(type(error).__name__) # FrozenInstanceErrorprice: float إنشاء Trade("AAPL", "abc", 1). فإذا كانت القيم السيئة ستفسد حسابات لاحقة بصمت فتحقق منها في __post_init__ وارفع ValueError فلا يمكن أن يوجد سجل معطوب. وأنشئ الكائنات دائمًا عبر الدالة البانية العادية، ومنها من from_line، حتى لا يمكن تخطي الفحص.@dataclassيحوّل الحقول الموسومة إلى__init__و__repr__و__eq__.- تأتي القيم الافتراضية أخيرًا؛ واستخدم
field(default_factory=list)للقوائم والقواميس. __post_init__تتحقق؛ و@classmethodتبني من أشكال أخرى؛ وfrozen=Trueيجعل القيم للقراءة فقط.
التالي: المشتقات في الكود، تقدير سرعة تغيّر كمية ما، وهي الفكرة وراء حساسيات الخيارات كدلتا وغاما.
المختبر: ما الذي يُقيَّم
- نظّف صفقة وتحقق منها باستخدام
__post_init__25 نقطة - احسب القيمة المتداولة باستخدام
notional25 نقطة - ابنِ صفقة من نص باستخدام
from_line25 نقطة - اجمع صافي صفقات رمز واحد باستخدام
net_quantity25 نقطة