पायथन 3.7 डेटाक्लासेस में वर्ग वंशानुक्रम


89

मैं वर्तमान में पायथन 3.7 में पेश किए गए नए डेटासलैक्स निर्माण पर अपने हाथ आजमा रहा हूं। मैं वर्तमान में एक मूल वर्ग की कुछ विरासत करने की कोशिश कर रहा हूं। ऐसा लगता है कि तर्कों के क्रम को मेरे वर्तमान दृष्टिकोण द्वारा बॉट किया गया है जैसे कि बाल वर्ग में बूल पैरामीटर को अन्य मापदंडों से पहले पारित किया जाता है। यह एक प्रकार की त्रुटि पैदा कर रहा है।

from dataclasses import dataclass

@dataclass
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')

@dataclass
class Child(Parent):
    school: str
    ugly: bool = True


jack = Parent('jack snr', 32, ugly=True)
jack_son = Child('jack jnr', 12, school = 'havard', ugly=True)

jack.print_id()
jack_son.print_id()

जब मैं इस कोड को चलाता हूं तो मुझे यह मिलता है TypeError:

TypeError: non-default argument 'school' follows default argument

मैं यह कैसे तय करुं?

जवाबों:


138

जिस तरह से डिटैचेलेसिस विशेषताओं को जोड़ती है वह आपको एक बेस क्लास में डिफॉल्ट्स के साथ एट्रिब्यूट्स का उपयोग करने में सक्षम होने से रोकता है और फिर एक उपवर्ग में डिफ़ॉल्ट (पोजिशनल एट्रिब्यूट्स) के बिना विशेषताओं का उपयोग करता है।

ऐसा इसलिए है क्योंकि विशेषताओं को एमआरओ के नीचे से शुरू करके संयुक्त किया जाता है, और पहले-देखे गए क्रम में विशेषताओं की एक क्रमबद्ध सूची का निर्माण किया जाता है; ओवरराइड को उनके मूल स्थान पर रखा जाता है। तो Parentशुरू होता है ['name', 'age', 'ugly'], जहां uglyएक डिफ़ॉल्ट है, और फिर उस सूची के अंत में Childजोड़ता ['school']है (सूची uglyमें पहले से ही)। इसका मतलब है कि आप के साथ समाप्त हो गया है ['name', 'age', 'ugly', 'school']और क्योंकि schoolडिफ़ॉल्ट नहीं है, इसके लिए अमान्य तर्क सूची में परिणाम होता है __init__

यह PEP-557 डेटाकालेज़ में प्रलेखित है , विरासत के तहत :

जब @dataclassडेकोरेटर द्वारा डेटा क्लास बनाया जा रहा है , तो यह रिवर्स MRO में क्लास के सभी बेस क्लासेस (जो कि शुरू होता है object) के माध्यम से दिखता है, और प्रत्येक डेटा क्लास के लिए, जो इसे पाता है, उस बेस क्लास से फ़ील्ड्स को एक ऑर्डर में जोड़ता है खेतों की मैपिंग। सभी बेस क्लास फ़ील्ड्स जोड़े जाने के बाद, यह अपने स्वयं के फ़ील्ड्स को ऑर्डर किए गए मैपिंग में जोड़ता है। उत्पन्न विधियों के सभी इस संयुक्त, गणना किए गए आदेशित फ़ील्ड का उपयोग करेंगे। क्योंकि फ़ील्ड सम्मिलन क्रम में हैं, व्युत्पन्न वर्ग बेस कक्षाओं को ओवरराइड करते हैं।

और विशिष्टता के तहत :

TypeErrorडिफ़ॉल्ट मान के बिना फ़ील्ड डिफ़ॉल्ट मान वाले फ़ील्ड का अनुसरण करता है, तो उठाया जाएगा। यह या तो तब सच होता है जब यह एकल वर्ग में होता है, या वर्ग की विरासत के परिणामस्वरूप होता है।

इस समस्या से बचने के लिए आपके पास यहां कुछ विकल्प हैं।

पहला विकल्प एमआरओ क्रम में बाद की स्थिति में डिफॉल्ट के साथ फ़ील्ड्स को बाध्य करने के लिए अलग बेस क्लास का उपयोग करना है। हर कीमत पर, उन वर्गों पर सीधे फ़ील्ड सेट करने से बचें जिन्हें बेस क्लास के रूप में उपयोग किया जाना है, जैसे Parent

निम्नलिखित वर्ग पदानुक्रम काम करता है:

# base classes with fields; fields without defaults separate from fields with.
@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

# public classes, deriving from base-with, base-without field classes
# subclasses of public classes should put the public base class up front.

@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

1
विस्तृत उत्तर के लिए बहुत बहुत धन्यवाद
मिस्टेरियो

यह बहुत मददगार है। मैं हालांकि mro के बारे में उलझन में हूँ। रनिंग प्रिंट (चाइल्ड। एमआरओ ()) मुझे मिलता है: [<वर्ग ' मुख्य। चिल्ड'>, <वर्ग ' मुख्य .Parent'>, <वर्ग ' मुख्य ._ सिल्डडिफॉल्ट्सबसे'>, <वर्ग ' मुख्य ' .ParentDefaultsBase '>, <। वर्ग ' main ._ChildBase'>, <class ' main ._ParentBase'>, <वर्ग 'ऑब्जेक्ट'>] तो क्या आधार वर्ग से पहले मूलभूत आधार नहीं हैं?
ओली

1
@ ठीक है कि सही क्रम है; ध्यान दें कि मैंने इसे अपने उत्तर में सूचीबद्ध किया है। जब आपके पास कई आधार वर्ग होते हैं, तो आपको यह तय करने के लिए कक्षाओं को रैखिक करने के लिए एक तरीका चाहिए होता है जो यह तय करता है कि विरासत में मिलने के समय कौन सी कक्षाएं दूसरों के सामने आती हैं। पायथन C3 रैखिककरण विधि का उपयोग करता है और मेरा जवाब इस बात का फायदा उठाता है कि यह कैसे काम करता है ताकि डिफॉल्ट के साथ हमेशा बिना किसी डिफॉल्ट के सभी विशेषताओं के बाद विशेषताओं को सुनिश्चित किया जा सके।
मार्टिन पीटर्स

वास्तव में, अटार्स काम कर सकते हैं लेकिन आपको उपयोग करने की आवश्यकता है attr.ib(kw_only=True), github.com/python-attrs/attrs/issues/38
laike9m

8

आप यह त्रुटि देख रहे हैं क्योंकि एक डिफ़ॉल्ट मान के बिना एक तर्क एक डिफ़ॉल्ट मान के साथ एक तर्क के बाद जोड़ा जा रहा है। डेटासल में विरासत में मिले क्षेत्रों का सम्मिलन क्रम विधि रिज़ॉल्यूशन ऑर्डर का उल्टा है , जिसका अर्थ है कि Parentफ़ील्ड पहले आते हैं, भले ही वे अपने बच्चों द्वारा बाद में लिखे गए हों।

PEP-557 से एक उदाहरण - डेटा वर्ग :

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

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

खेतों की अंतिम सूची क्रम में है x, y, z। वर्ग में निर्दिष्ट अंतिम प्रकार xहै ।intC

दुर्भाग्य से, मुझे नहीं लगता कि इसके आसपास कोई रास्ता है। मेरी समझ यह है कि अगर अभिभावक वर्ग का डिफ़ॉल्ट तर्क है, तो किसी भी बच्चे के वर्ग में गैर-डिफ़ॉल्ट तर्क नहीं हो सकते हैं।


मुझे लगता है कि नॉन डिफॉल्ट तर्क को डिफॉल्ट से पहले आना चाहिए लेकिन जब बच्चे की दलीलें जोड़ने से पहले अभिभावक बहस करते हैं तो यह कैसे हो सकता है?
मिस्टीरियो

3
मुझे नहीं लगता कि दुर्भाग्य से इसके आसपास कोई रास्ता है। मेरी समझ यह है कि अगर अभिभावक वर्ग का डिफ़ॉल्ट तर्क है, तो किसी भी बच्चे के वर्ग में गैर-डिफ़ॉल्ट तर्क नहीं हो सकते हैं।
पैट्रिक हॉग

1
क्या आप उस जानकारी को उत्तर में जोड़ने से पहले ही उसे चिन्हित कर सकते हैं? यह एक दिन किसी की मदद करेगा। यह काफी दुर्भाग्यपूर्ण है कि डिटैचेलेस की सीमा। यह मेरे वर्तमान अजगर परियोजना को प्रस्तुत करता है। इस तरह के कार्यान्वयन को देखना अच्छा है
मिस्टेरियो

6

यदि आप उन्हें init फ़ंक्शन से बाहर करते हैं, तो आप पैरेंट कक्षाओं में डिफॉल्ट के साथ विशेषताओं का उपयोग कर सकते हैं। यदि आपको डिफ़ॉल्ट इनिट को ओवरराइड करने की संभावना है, तो प्रवीण कुलकर्णी के उत्तर के साथ कोड का विस्तार करें।

from dataclasses import dataclass, field

@dataclass
class Parent:
    name: str
    age: int
    ugly: bool = field(default=False, init=False)

@dataclass
class Child(Parent):
    school: str

jack = Parent('jack snr', 32)
jack_son = Child('jack jnr', 12, school = 'havard')
jack_son.ugly = True

मुझे लगता है कि इस उत्तर को अधिक मान्यता दी जानी चाहिए। यह मूल वर्ग में एक डिफ़ॉल्ट फ़ील्ड होने की समस्या को हल करता है, इस प्रकार TypeError को हटा देता है।
निल्स बेंग्सटन

5

Martijn Pieters समाधान के आधार पर मैंने निम्नलिखित किया:

1) पोस्ट_इनिट को लागू करने वाला एक मिश्रण बनाएं

from dataclasses import dataclass

no_default = object()


@dataclass
class NoDefaultAttributesPostInitMixin:

    def __post_init__(self):
        for key, value in self.__dict__.items():
            if value is no_default:
                raise TypeError(
                    f"__init__ missing 1 required argument: '{key}'"
                )

2) तब विरासत की समस्या के साथ कक्षाओं में:

from src.utils import no_default, NoDefaultAttributesChild

@dataclass
class MyDataclass(DataclassWithDefaults, NoDefaultAttributesPostInitMixin):
    attr1: str = no_default

संपादित करें:

एक समय के बाद मुझे इस समाधान के साथ mypy के साथ समस्याएं भी आती हैं, निम्न कोड समस्या को ठीक करता है।

from dataclasses import dataclass
from typing import TypeVar, Generic, Union

T = TypeVar("T")


class NoDefault(Generic[T]):
    ...


NoDefaultVar = Union[NoDefault[T], T]
no_default: NoDefault = NoDefault()


@dataclass
class NoDefaultAttributesPostInitMixin:
    def __post_init__(self):
        for key, value in self.__dict__.items():
            if value is NoDefault:
                raise TypeError(f"__init__ missing 1 required argument: '{key}'")


@dataclass
class Parent(NoDefaultAttributesPostInitMixin):
    a: str = ""

@dataclass
class Child(Foo):
    b: NoDefaultVar[str] = no_default

क्या आपने "वर्ग MyDataclass (DataclassWithDefaults, NoDefaultAttributesPostInitMixin)" 2 से ऊपर) लिखने का इरादा किया है?
स्कॉट पी।

5

नीचे का दृष्टिकोण शुद्ध पायथन dataclassesऔर बहुत बॉयलरप्लेट कोड का उपयोग करते हुए इस समस्या से निपटता है ।

ugly_init: dataclasses.InitVar[bool]एक रूप में कार्य करता छद्म क्षेत्र बस हमें प्रारंभ करते हैं और एक बार उदाहरण बनाई गई है खो जाएगा मदद करने के लिए। जबकि ugly: bool = field(init=False)एक उदाहरण सदस्य है जिसके द्वारा आरंभ नहीं किया जा जाएगा __init__विधि लेकिन वैकल्पिक रूप से का उपयोग कर प्रारंभ किया जा सकता है __post_init__विधि (आप और अधिक प्राप्त कर सकते हैं यहाँ ।)।

from dataclasses import dataclass, field

@dataclass
class Parent:
    name: str
    age: int
    ugly: bool = field(init=False)
    ugly_init: dataclasses.InitVar[bool]

    def __post_init__(self, ugly_init: bool):
        self.ugly = ugly_init

    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):
    school: str

jack = Parent('jack snr', 32, ugly_init=True)
jack_son = Child('jack jnr', 12, school='havard', ugly_init=True)

jack.print_id()
jack_son.print_id()

यदि आप एक ऐसे पैटर्न का उपयोग करना चाहते हैं जहां ugly_initवैकल्पिक हो, तो आप मूल ugly_initपैरामीटर पर एक क्लास पद्धति को परिभाषित कर सकते हैं जिसमें एक वैकल्पिक पैरामीटर शामिल है:

from dataclasses import dataclass, field, InitVar

@dataclass
class Parent:
    name: str
    age: int
    ugly: bool = field(init=False)
    ugly_init: InitVar[bool]

    def __post_init__(self, ugly_init: bool):
        self.ugly = ugly_init
    
    @classmethod
    def create(cls, ugly_init=True, **kwargs):
        return cls(ugly_init=ugly_init, **kwargs)

    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):
    school: str

jack = Parent.create(name='jack snr', age=32, ugly_init=False)
jack_son = Child.create(name='jack jnr', age=12, school='harvard')

jack.print_id()
jack_son.print_id()

अब आप createपैरेंट / चाइल्ड क्लासेस बनाने के लिए क्लास विधि का उपयोग फ़ैक्टरी मान के लिए डिफ़ॉल्ट मान के साथ कर सकते हैं ugly_init। ध्यान दें कि आपको काम करने के लिए इस दृष्टिकोण के लिए नामित मापदंडों का उपयोग करना चाहिए।


ugly_init अब एक आवश्यक पैरामीटर है जिसमें कोई डिफ़ॉल्ट नहीं है
Vadym Tyemirov

2

मैं इस सवाल पर वापस आया कि खोजाचलेसेस को एक डेकोरेटर पैरामीटर मिल सकता है जो खेतों को फिर से व्यवस्थित करने की अनुमति देता है। यह निश्चित रूप से एक आशाजनक विकास है, हालांकि इस सुविधा पर विकास कुछ हद तक रुका हुआ है।

अभी, आप इस व्यवहार को प्राप्त कर सकते हैं, साथ ही कुछ अन्य बारीकियों को भी डाटाकाल्सी का उपयोग करके , इस तरह की कुंठाओं पर काबू पाने वाले डाटाकैलेज़ के मेरे पुन: कार्यान्वयन। मूल उदाहरण के from dataclassyस्थान पर उपयोग करने का from dataclassesमतलब है कि यह त्रुटियों के बिना चलता है।

स्पष्ट क्या हो रहा है के हस्ताक्षर को मुद्रित करने के लिए निरीक्षण का उपयोग करना Child; परिणाम है (name: str, age: int, school: str, ugly: bool = True)। फ़ील्ड्स को हमेशा के लिए पुन: व्यवस्थित किया जाता है ताकि डिफ़ॉल्ट मान वाले फ़ील्ड को उनके बिना फ़ील्ड के बाद आने वाले पैरामीटर में इनिशियलाइज़र में लाया जा सके। दोनों सूचियाँ (बिना डिफॉल्ट के क्षेत्र, और उन लोगों के साथ) अभी भी परिभाषा क्रम में क्रमबद्ध हैं।

इस मुद्दे पर आमने सामने आना उन कारकों में से एक था, जिसने मुझे डेटासलैक्स के लिए प्रतिस्थापन लिखने के लिए प्रेरित किया। यहाँ सहायक, जबकि सहायक, विस्तृत है, कोड की आवश्यकता इस हद तक होती है कि वे पूरी तरह से पठनीयता लाभ डेटाकेस के भोले दृष्टिकोण (जिससे क्षेत्र क्रमिक रूप से अनुमानित पूर्वानुमान है) को पूरी तरह से नकार देते हैं।


1

एक संभावित काम के आसपास मूल क्षेत्रों को जोड़ने के लिए बंदर-पैचिंग का उपयोग करना है

import dataclasses as dc

def add_args(parent): 
    def decorator(orig):
        "Append parent's fields AFTER orig's fields"

        # Aggregate fields
        ff  = [(f.name, f.type, f) for f in dc.fields(dc.dataclass(orig))]
        ff += [(f.name, f.type, f) for f in dc.fields(dc.dataclass(parent))]

        new = dc.make_dataclass(orig.__name__, ff)
        new.__doc__ = orig.__doc__

        return new
    return decorator

class Animal:
    age: int = 0 

@add_args(Animal)
class Dog:
    name: str
    noise: str = "Woof!"

@add_args(Animal)
class Bird:
    name: str
    can_fly: bool = True

Dog("Dusty", 2)               # --> Dog(name='Dusty', noise=2, age=0)
b = Bird("Donald", False, 40) # --> Bird(name='Donald', can_fly=False, age=40)

चेक करने से गैर-डिफ़ॉल्ट फ़ील्ड को प्रीपेन्ड करना भी संभव है if f.default is dc.MISSING, लेकिन यह संभवतः बहुत गंदा है।

जबकि बंदर-पेटिंग में वंशानुक्रम की कुछ विशेषताओं का अभाव है, फिर भी इसका उपयोग सभी छद्म-बाल वर्गों में विधियों को जोड़ने के लिए किया जा सकता है।

अधिक ठीक-ठीक नियंत्रण के लिए, डिफ़ॉल्ट मानों का उपयोग करके सेट करें dc.field(compare=False, repr=True, ...)


1

आप एक संशोधित संस्करण का उपयोग कर सकते हैं dataclasses, जो केवल एक कीवर्ड उत्पन्न करेगा __init__:

import dataclasses


def _init_fn(fields, frozen, has_post_init, self_name):
    # fields contains both real fields and InitVar pseudo-fields.
    globals = {'MISSING': dataclasses.MISSING,
               '_HAS_DEFAULT_FACTORY': dataclasses._HAS_DEFAULT_FACTORY}

    body_lines = []
    for f in fields:
        line = dataclasses._field_init(f, frozen, globals, self_name)
        # line is None means that this field doesn't require
        # initialization (it's a pseudo-field).  Just skip it.
        if line:
            body_lines.append(line)

    # Does this class have a post-init function?
    if has_post_init:
        params_str = ','.join(f.name for f in fields
                              if f._field_type is dataclasses._FIELD_INITVAR)
        body_lines.append(f'{self_name}.{dataclasses._POST_INIT_NAME}({params_str})')

    # If no body lines, use 'pass'.
    if not body_lines:
        body_lines = ['pass']

    locals = {f'_type_{f.name}': f.type for f in fields}
    return dataclasses._create_fn('__init__',
                      [self_name, '*'] + [dataclasses._init_param(f) for f in fields if f.init],
                      body_lines,
                      locals=locals,
                      globals=globals,
                      return_type=None)


def add_init(cls, frozen):
    fields = getattr(cls, dataclasses._FIELDS)

    # Does this class have a post-init function?
    has_post_init = hasattr(cls, dataclasses._POST_INIT_NAME)

    # Include InitVars and regular fields (so, not ClassVars).
    flds = [f for f in fields.values()
            if f._field_type in (dataclasses._FIELD, dataclasses._FIELD_INITVAR)]
    dataclasses._set_new_attribute(cls, '__init__',
                       _init_fn(flds,
                                frozen,
                                has_post_init,
                                # The name to use for the "self"
                                # param in __init__.  Use "self"
                                # if possible.
                                '__dataclass_self__' if 'self' in fields
                                else 'self',
                                ))

    return cls


# a dataclass with a constructor that only takes keyword arguments
def dataclass_keyword_only(_cls=None, *, repr=True, eq=True, order=False,
              unsafe_hash=False, frozen=False):
    def wrap(cls):
        cls = dataclasses.dataclass(
            cls, init=False, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen)
        return add_init(cls, frozen)

    # See if we're being called as @dataclass or @dataclass().
    if _cls is None:
        # We're called with parens.
        return wrap

    # We're called as @dataclass without parens.
    return wrap(_cls)

(यह भी एक के रूप में तैनात सार , अजगर 3.6 backport के साथ परीक्षण)

इसके लिए बाल वर्ग को परिभाषित करना होगा

@dataclass_keyword_only
class Child(Parent):
    school: str
    ugly: bool = True

और उत्पन्न होगा __init__(self, *, name:str, age:int, ugly:bool=True, school:str)(जो वैध अजगर है)। यहाँ केवल कैविएट वस्तुओं को स्थितिजन्य तर्कों के साथ आरंभ करने की अनुमति नहीं दे रहा है, लेकिन अन्यथा यह पूरी तरह से नियमित है dataclassजिसमें कोई बदसूरत हैक नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.