जिस तरह से डिटैचेलेसिस विशेषताओं को जोड़ती है वह आपको एक बेस क्लास में डिफॉल्ट्स के साथ एट्रिब्यूट्स का उपयोग करने में सक्षम होने से रोकता है और फिर एक उपवर्ग में डिफ़ॉल्ट (पोजिशनल एट्रिब्यूट्स) के बिना विशेषताओं का उपयोग करता है।
ऐसा इसलिए है क्योंकि विशेषताओं को एमआरओ के नीचे से शुरू करके संयुक्त किया जाता है, और पहले-देखे गए क्रम में विशेषताओं की एक क्रमबद्ध सूची का निर्माण किया जाता है; ओवरराइड को उनके मूल स्थान पर रखा जाता है। तो Parent
शुरू होता है ['name', 'age', 'ugly']
, जहां ugly
एक डिफ़ॉल्ट है, और फिर उस सूची के अंत में Child
जोड़ता ['school']
है (सूची ugly
में पहले से ही)। इसका मतलब है कि आप के साथ समाप्त हो गया है ['name', 'age', 'ugly', 'school']
और क्योंकि school
डिफ़ॉल्ट नहीं है, इसके लिए अमान्य तर्क सूची में परिणाम होता है __init__
।
यह PEP-557 डेटाकालेज़ में प्रलेखित है , विरासत के तहत :
जब @dataclass
डेकोरेटर द्वारा डेटा क्लास बनाया जा रहा है , तो यह रिवर्स MRO में क्लास के सभी बेस क्लासेस (जो कि शुरू होता है object
) के माध्यम से दिखता है, और प्रत्येक डेटा क्लास के लिए, जो इसे पाता है, उस बेस क्लास से फ़ील्ड्स को एक ऑर्डर में जोड़ता है खेतों की मैपिंग। सभी बेस क्लास फ़ील्ड्स जोड़े जाने के बाद, यह अपने स्वयं के फ़ील्ड्स को ऑर्डर किए गए मैपिंग में जोड़ता है। उत्पन्न विधियों के सभी इस संयुक्त, गणना किए गए आदेशित फ़ील्ड का उपयोग करेंगे। क्योंकि फ़ील्ड सम्मिलन क्रम में हैं, व्युत्पन्न वर्ग बेस कक्षाओं को ओवरराइड करते हैं।
और विशिष्टता के तहत :
TypeError
डिफ़ॉल्ट मान के बिना फ़ील्ड डिफ़ॉल्ट मान वाले फ़ील्ड का अनुसरण करता है, तो उठाया जाएगा। यह या तो तब सच होता है जब यह एकल वर्ग में होता है, या वर्ग की विरासत के परिणामस्वरूप होता है।
इस समस्या से बचने के लिए आपके पास यहां कुछ विकल्प हैं।
पहला विकल्प एमआरओ क्रम में बाद की स्थिति में डिफॉल्ट के साथ फ़ील्ड्स को बाध्य करने के लिए अलग बेस क्लास का उपयोग करना है। हर कीमत पर, उन वर्गों पर सीधे फ़ील्ड सेट करने से बचें जिन्हें बेस क्लास के रूप में उपयोग किया जाना है, जैसे Parent
।
निम्नलिखित वर्ग पदानुक्रम काम करता है:
@dataclass
class _ParentBase:
name: str
age: int
@dataclass
class _ParentDefaultsBase:
ugly: bool = False
@dataclass
class _ChildBase(_ParentBase):
school: str
@dataclass
class _ChildDefaultsBase(_ParentDefaultsBase):
ugly: bool = True
@dataclass
class Parent(_ParentDefaultsBase, _ParentBase):
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f"The Name is {self.name} and {self.name} is {self.age} year old")
@dataclass
class Child(Parent, _ChildDefaultsBase, _ChildBase):
pass
डिफॉल्ट के बिना फ़ील्ड के साथ अलग बेस क्लासेस और डिफॉल्ट वाले फ़ील्ड्स से फ़ील्ड्स को बाहर निकालकर , और सावधानीपूर्वक चुने गए इनहेरिटेंस ऑर्डर से, आप उन एमआरओ का उत्पादन कर सकते हैं जो डिफॉल्ट वाले उन सभी क्षेत्रों को बिना डिफॉल्ट के सामने रखते हैं। के लिए उलटा MRO (अनदेखा object
) Child
है:
_ParentBase
_ChildBase
_ParentDefaultsBase
_ChildDefaultsBase
Parent
ध्यान दें कि Parent
कोई भी नया फ़ील्ड सेट नहीं करता है, इसलिए यहां कोई बात नहीं है कि यह फ़ील्ड सूचीकरण क्रम में 'अंतिम' है। डिफ़ॉल्ट (बिना क्षेत्रों के साथ वर्गों _ParentBase
और _ChildBase
) चूक (साथ क्षेत्रों के साथ कक्षाएं पूर्व में होना _ParentDefaultsBase
और _ChildDefaultsBase
)।
परिणाम एक पुराने क्षेत्र के साथ वर्ग Parent
और Child
वर्ग है, जबकि Child
अभी भी इसका एक उपवर्ग है Parent
:
>>> from inspect import signature
>>> signature(Parent)
<Signature (name: str, age: int, ugly: bool = False) -> None>
>>> signature(Child)
<Signature (name: str, age: int, school: str, ugly: bool = True) -> None>
>>> issubclass(Child, Parent)
True
और इसलिए आप दोनों वर्गों के उदाहरण बना सकते हैं:
>>> jack = Parent('jack snr', 32, ugly=True)
>>> jack_son = Child('jack jnr', 12, school='havard', ugly=True)
>>> jack
Parent(name='jack snr', age=32, ugly=True)
>>> jack_son
Child(name='jack jnr', age=12, school='havard', ugly=True)
एक अन्य विकल्प केवल चूक वाले क्षेत्रों का उपयोग करना है; आप अभी भी एक school
मूल्य की आपूर्ति नहीं करने के लिए एक त्रुटि कर सकते हैं __post_init__
:
_no_default = object()
@dataclass
class Child(Parent):
school: str = _no_default
ugly: bool = True
def __post_init__(self):
if self.school is _no_default:
raise TypeError("__init__ missing 1 required argument: 'school'")
लेकिन यह क्षेत्र क्रम को बदल देता है; school
के बाद समाप्त होता है ugly
:
<Signature (name: str, age: int, ugly: bool = True, school: str = <object object at 0x1101d1210>) -> None>
और एक प्रकार का संकेत चेकर एक स्ट्रिंग नहीं होने के बारे में शिकायत करेगा_no_default
।
आप उस attrs
परियोजना का उपयोग भी कर सकते हैं , जो परियोजना प्रेरित थी dataclasses
। यह एक अलग वंशानुक्रम विलय की रणनीति का उपयोग करता है; यह एक उपवर्ग में ओवरराइड किए गए फ़ील्ड्स को फ़ील्ड सूची के अंत तक खींचता है, इसलिए कक्षा ['name', 'age', 'ugly']
में Parent
वर्ग बन जाता ['name', 'age', 'school', 'ugly']
है Child
; डिफ़ॉल्ट रूप से फ़ील्ड attrs
को ओवरराइड करके , MRO नृत्य करने की आवश्यकता के बिना ओवरराइड की अनुमति देता है।
attrs
प्रकार संकेत के बिना परिभाषित क्षेत्रों का समर्थन करता है, लेकिन सेटिंग द्वारा समर्थित प्रकार संकेत मोड से चिपके रहने देता है auto_attribs=True
:
import attr
@attr.s(auto_attribs=True)
class Parent:
name: str
age: int
ugly: bool = False
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f"The Name is {self.name} and {self.name} is {self.age} year old")
@attr.s(auto_attribs=True)
class Child(Parent):
school: str
ugly: bool = True