डेटा कक्षाएं क्या हैं और वे आम कक्षाओं से कैसे भिन्न हैं?


141

पीईपी 557 के साथ डेटा क्लासेस को अजगर मानक पुस्तकालय में पेश किया गया है।

वे का उपयोग करते हैं @dataclass डेकोरेटर करते हैं और उन्हें "डिफ़ॉल्ट के साथ परस्पर नामांकित" माना जाता है, लेकिन मुझे वास्तव में यकीन नहीं है कि मैं समझता हूं कि इसका वास्तव में क्या मतलब है और वे आम कक्षाओं से अलग कैसे हैं।

अजगर डेटा वर्ग वास्तव में क्या हैं और उनका उपयोग करना कब सबसे अच्छा है?


8
PEP की व्यापक सामग्री को देखते हुए, आप और क्या जानना चाहते हैं? namedtuples अपरिवर्तनीय हैं और उनमें विशेषताओं के लिए डिफ़ॉल्ट मान नहीं हो सकते हैं, जबकि डेटा वर्ग परस्पर हैं और उनके पास हो सकते हैं।
जोंशरशिप

31
@jonrsharpe मुझे उचित लगता है कि विषय पर स्टैकओवरफ़्लो धागा होना चाहिए। Stackoverflow को Q & A प्रारूप में एक विश्वकोश माना जाता है, नहीं? जवाब कभी नहीं "बस इस अन्य वेबसाइट पर देखो।" यहाँ नीचे नहीं होना चाहिए था।
ल्यूक डेविस

12
किसी सूची में किसी आइटम को कैसे जोड़ा जाए, इस पर पांच सूत्र हैं। @dataclassसाइट पर एक प्रश्न विघटित होने का कारण नहीं होगा।
एरिक

2
@jonrsharpe namedtuplesमें डिफ़ॉल्ट मान हो सकते हैं। यहाँ एक नज़र डालें: stackoverflow.com/questions/11351032/…
MJB

जवाबों:


152

डेटा कक्षाएं सिर्फ नियमित कक्षाएं हैं जो भंडारण की स्थिति के लिए तैयार हैं, जिनमें बहुत से तर्क हैं। हर बार जब आप एक वर्ग बनाते हैं, जिसमें अधिकतर वे विशेषताएँ होती हैं, जिन्हें आपने डेटा वर्ग बनाया है।

dataclassesमॉड्यूल क्या करता है यह आसान है डेटा क्लासेस बनाना जाता है। यह आपके लिए बहुत सारे बॉयलर प्लेट की देखभाल करता है।

यह विशेष रूप से महत्वपूर्ण है जब आपके डेटा वर्ग को धोने योग्य होना चाहिए; इसके लिए एक __hash__विधि के साथ-साथ एक __eq__विधि की आवश्यकता होती है । यदि आप __repr__डिबगिंग में आसानी के लिए एक कस्टम विधि जोड़ते हैं , तो यह काफी महत्वपूर्ण हो सकता है:

class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def __init__(
            self, 
            name: str, 
            unit_price: float,
            quantity_on_hand: int = 0
        ) -> None:
        self.name = name
        self.unit_price = unit_price
        self.quantity_on_hand = quantity_on_hand

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

    def __repr__(self) -> str:
        return (
            'InventoryItem('
            f'name={self.name!r}, unit_price={self.unit_price!r}, '
            f'quantity_on_hand={self.quantity_on_hand!r})'

    def __hash__(self) -> int:
        return hash((self.name, self.unit_price, self.quantity_on_hand))

    def __eq__(self, other) -> bool:
        if not isinstance(other, InventoryItem):
            return NotImplemented
        return (
            (self.name, self.unit_price, self.quantity_on_hand) == 
            (other.name, other.unit_price, other.quantity_on_hand))

इसके साथ dataclassesआप इसे कम कर सकते हैं:

from dataclasses import dataclass

@dataclass(unsafe_hash=True)
class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

एक ही कक्षा डेकोरेटर भी तुलना विधियों (उत्पन्न कर सकते हैं __lt__, __gt__आदि) और संभाल अचल स्थिति।

namedtupleकक्षाएं भी डेटा वर्ग हैं, लेकिन डिफ़ॉल्ट रूप से अपरिवर्तनीय हैं (साथ ही अनुक्रम भी)। dataclassesइस संबंध में बहुत अधिक लचीले हैं, और आसानी से इस तरह की संरचना की जा सकती है कि वे एक namedtupleवर्ग के रूप में एक ही भूमिका भर सकते हैं ।

पीईपी परियोजना से प्रेरित थाattrs , जो और भी अधिक कर सकता है (स्लॉट, सत्यापनकर्ता, कन्वर्टर्स, मेटाडेटा, आदि सहित)।

यदि आप कुछ उदाहरण देखना चाहते हैं, तो मैंने हाल ही dataclassesमें अपने कई एडवेंट कोड समाधानों के लिए उपयोग किया है , दिन 7 , दिन 8 , दिन 11 और दिन 20 के समाधान देखें ।

यदि आप dataclassesपायथन संस्करणों <3.7 में मॉड्यूल का उपयोग करना चाहते हैं , तो आप बैकपोर्ट वाले मॉड्यूल (3.6 की आवश्यकता है) को स्थापित कर सकते हैं या attrsऊपर वर्णित परियोजना का उपयोग कर सकते हैं ।


2
पहले उदाहरण में क्या आप जानबूझकर उसी नाम के सदस्यों के साथ वर्ग के सदस्यों को छिपाते हैं? कृपया इस मुहावरे को समझने में मदद करें।
व्लादिमीरलेन

4
@ व्लादिमीर लीनिन: कोई वर्ग गुण नहीं हैं, केवल प्रकार के एनोटेशन हैं। देखें पीईपी 526 , विशेष रूप से कक्षा और उदाहरण चर एनोटेशन अनुभाग
मार्टिन पीटर्स

1
@Bananach: डिफ़ॉल्ट मान के साथ एक कीवर्ड तर्क के साथ @dataclassलगभग एक ही __init__विधि उत्पन्न करता है quantity_on_hand। जब आप एक उदाहरण बनाते हैं, तो यह quantity_on_handहमेशा इंस्टेंस विशेषता को सेट करेगा । तो मेरा पहला , नॉन-डिटैचेस का उदाहरण उसी पैटर्न का उपयोग करता है कि डिटैक्लास जनरेट किए गए कोड को क्या करेगा।
मार्टिन पीटर्स

1
@Ananach: इसलिए पहले उदाहरण में, हम सिर्फ एक इंस्टेंस विशेषता को सेट करके छोड़ सकते हैं और क्लास की विशेषता को छाया नहीं दे सकते, यह किसी भी तरह से उस अर्थ में सेटिंग को बेमानी है, लेकिन डेटाकेस इसे सेट करते हैं।
मार्टिन पीटर्स

1
@ user2853437 आपका उपयोग मामला वास्तव में डेटाकेस द्वारा समर्थित नहीं है; शायद आप, dataclasses 'बड़ा चचेरे भाई का उपयोग कर बंद बेहतर होगा attrs । यह परियोजना प्रति फ़ील्ड कन्वर्टर्स का समर्थन करती है जो आपको फ़ील्ड मान को सामान्य करने देती है। यदि आप डेटासलैस के साथ रहना चाहते हैं, तो हां, __post_init__विधि में सामान्यीकरण करें ।
Martijn Pieters

62

अवलोकन

सवाल का समाधान किया गया है। हालाँकि, यह उत्तर डेटैकल्स की बुनियादी समझ में सहायता करने के लिए कुछ व्यावहारिक उदाहरण जोड़ता है।

अजगर डेटा वर्ग वास्तव में क्या हैं और उनका उपयोग करना कब सबसे अच्छा है?

  1. कोड जनरेटर : बॉयलरप्लेट कोड उत्पन्न करते हैं; आप नियमित कक्षा में विशेष विधियों को लागू करने का विकल्प चुन सकते हैं या डेटाक्लास उन्हें स्वचालित रूप से लागू कर सकते हैं।
  2. डेटा कंटेनर : संरचनाएं जो डेटा रखती हैं (उदाहरण के लिए ट्यूपल्स और डीकट्स), अक्सर बिंदीदार, विशेषता पहुंच जैसे कि कक्षाएं, namedtupleऔर अन्य

"डिफ़ॉल्ट के साथ परस्पर नामांकित नाम [s]"

यहाँ बाद वाले वाक्यांश का अर्थ है:

  • परिवर्तनशील : डिफ़ॉल्ट रूप से, डिटैचेस विशेषताओं को पुन: असाइन किया जा सकता है। आप वैकल्पिक रूप से उन्हें अपरिवर्तनीय बना सकते हैं (नीचे उदाहरण देखें)।
  • nametuple : आपने बिंदी लगाई है , विशेषता एक्सेस ए की तरहnamedtuple या एक नियमित वर्ग की ।
  • चूक : आप विशेषताओं के लिए डिफ़ॉल्ट मान असाइन कर सकते हैं।

आम कक्षाओं की तुलना में, आप मुख्य रूप से बॉयलरप्लेट कोड टाइप करने पर बचत करते हैं।


विशेषताएं

यह डेटाक्लास सुविधाओं का एक अवलोकन है (टीएल; डीआर। अगले अनुभाग में सारांश तालिका देखें)।

क्या आपको मिला

यहां ऐसी विशेषताएं हैं जिन्हें आप डीटैक्लास से डिफ़ॉल्ट रूप से प्राप्त करते हैं।

गुण + प्रतिनिधित्व + तुलना

import dataclasses


@dataclasses.dataclass
#@dataclasses.dataclass()                                       # alternative
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

ये डिफॉल्ट निम्नलिखित कीवर्ड को स्वचालित रूप से सेट करके प्रदान किए जाते हैं True:

@dataclasses.dataclass(init=True, repr=True, eq=True)

आप क्या चालू कर सकते हैं

यदि उपयुक्त कीवर्ड सेट हैं तो अतिरिक्त सुविधाएँ उपलब्ध हैं True

गण

@dataclasses.dataclass(order=True)
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

आदेश देने के तरीके अब लागू किए गए हैं (ऑपरेटरों को ओवरलोडिंग:) < > <= >=, इसी तरह functools.total_orderingमजबूत समानता परीक्षणों के साथ।

धोने योग्य, म्यूटेबल

@dataclasses.dataclass(unsafe_hash=True)                        # override base `__hash__`
class Color:
    ...

यद्यपि ऑब्जेक्ट संभावित रूप से उत्परिवर्तित (संभवतः अवांछित) है, एक हैश कार्यान्वित किया जाता है।

धोने योग्य, अपरिवर्तनीय

@dataclasses.dataclass(frozen=True)                             # `eq=True` (default) to be immutable 
class Color:
    ...

एक हैश अब लागू हो गया है और ऑब्जेक्ट को बदलना या विशेषताओं को असाइन करना अस्वीकृत है।

कुल मिलाकर, अगर वस्तु unsafe_hash=Trueया तो धोने योग्य है या frozen=True

अधिक विवरण के साथ मूल हैशिंग लॉजिक टेबल भी देखें।

जो आपको नहीं मिलता

निम्नलिखित विशेषताएं प्राप्त करने के लिए, विशेष विधियों को मैन्युअल रूप से लागू किया जाना चाहिए:

unpacking

@dataclasses.dataclass
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

    def __iter__(self):
        yield from dataclasses.astuple(self)

अनुकूलन

@dataclasses.dataclass
class SlottedColor:
    __slots__ = ["r", "b", "g"]
    r : int
    g : int
    b : int

ऑब्जेक्ट का आकार अब कम हो गया है:

>>> imp sys
>>> sys.getsizeof(Color)
1056
>>> sys.getsizeof(SlottedColor)
888

कुछ परिस्थितियों में, __slots__इंस्टेंस बनाने और एसेसिंग एक्सेस की गति में भी सुधार होता है। इसके अलावा, स्लॉट डिफ़ॉल्ट असाइनमेंट की अनुमति नहीं देते हैं; अन्यथा, एक ValueErrorउठाया जाता है।

इस ब्लॉग पोस्ट में स्लॉट्स पर अधिक देखें ।


सारांश तालिका

+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
|       Feature        |       Keyword        |                      Example                       |           Implement in a Class          |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Attributes           |  init                |  Color().r -> 0                                    |  __init__                               |
| Representation       |  repr                |  Color() -> Color(r=0, g=0, b=0)                   |  __repr__                               |
| Comparision*         |  eq                  |  Color() == Color(0, 0, 0) -> True                 |  __eq__                                 |
|                      |                      |                                                    |                                         |
| Order                |  order               |  sorted([Color(0, 50, 0), Color()]) -> ...         |  __lt__, __le__, __gt__, __ge__         |
| Hashable             |  unsafe_hash/frozen  |  {Color(), {Color()}} -> {Color(r=0, g=0, b=0)}    |  __hash__                               |
| Immutable            |  frozen + eq         |  Color().r = 10 -> TypeError                       |  __setattr__, __delattr__               |
|                      |                      |                                                    |                                         |
| Unpacking+           |  -                   |  r, g, b = Color()                                 |   __iter__                              |
| Optimization+        |  -                   |  sys.getsizeof(SlottedColor) -> 888                |  __slots__                              |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+

+ इन विधियों को स्वचालित रूप से उत्पन्न नहीं किया जाता है और एक डेटासाल में मैनुअल कार्यान्वयन की आवश्यकता होती है।

* __ne__ जरूरत नहीं है और इस तरह लागू नहीं है


अतिरिक्त सुविधाये

पोस्ट-आरंभीकरण

@dataclasses.dataclass
class RGBA:
    r : int = 0
    g : int = 0
    b : int = 0
    a : float = 1.0

    def __post_init__(self):
        self.a : int =  int(self.a * 255)


RGBA(127, 0, 255, 0.5)
# RGBA(r=127, g=0, b=255, a=127)

विरासत

@dataclasses.dataclass
class RGBA(Color):
    a : int = 0

रूपांतरण

एक टपल या एक ताना, पुनरावर्ती :

>>> dataclasses.astuple(Color(128, 0, 255))
(128, 0, 255)
>>> dataclasses.asdict(Color(128, 0, 255))
{r: 128, g: 0, b: 255}

सीमाएं


संदर्भ

  • डेटासलैक्स पर आर। हेटिंगर की बात : सभी कोड जनरेटर को समाप्त करने के लिए कोड जनरेटर
  • टी Hunner के बात पर सब cruft बिना अजगर क्लास: आसान कक्षाएं
  • हैशिंग विवरण पर अजगर का प्रलेखन
  • रियल पायथन के गाइड पर पायथन 3.7 में डाटा वर्ग को द अल्टिमेट गाइड
  • ए। शॉ का ब्लॉग पाइथन 3.7 डेटा क्लासेस के संक्षिप्त दौरे पर पोस्ट है
  • ई। स्मिथ की गिथब रिपॉजिटरी ऑन डाटाकेल्स

2

से पीईपी विनिर्देश :

एक क्लास डेकोरेटर प्रदान किया जाता है जो PEP 526 में परिभाषित प्रकार एनोटेशन के साथ चर के लिए एक वर्ग परिभाषा का निरीक्षण करता है, "वैरिएंट एनोटेशन के लिए सिंटैक्स"। इस दस्तावेज़ में, ऐसे चर को फ़ील्ड कहा जाता है। इन क्षेत्रों का उपयोग करते हुए, डेकोरेटर वर्ग में उत्पन्न विधि परिभाषाओं को जोड़ता है ताकि उदाहरण आरंभीकरण, एक repr, तुलना विधियों, और वैकल्पिक रूप से अन्य तरीकों को निर्दिष्ट किया जा सके जैसा कि विनिर्देश अनुभाग में वर्णित है। इस तरह की क्लास को डेटा क्लास कहा जाता है, लेकिन क्लास के बारे में वास्तव में कुछ खास नहीं है: डेकोरेटर क्लास में उत्पन्न तरीकों को जोड़ता है और उसी क्लास को वापस करता है जो उसे दिया गया था।

@dataclassजनरेटर वर्ग कि आप अन्यथा की तरह अपने आप को निर्धारित करेंगे करने के तरीकों कहते हैं __repr__, __init__, __lt__, और __gt__


2

इस सरल वर्ग पर विचार करें Foo

from dataclasses import dataclass
@dataclass
class Foo:    
    def bar():
        pass  

यहाँ dir()अंतर्निहित तुलना है। बायीं ओर Foo@dataclass डेकोरेटर के बिना है, और दाईं ओर @dataclass डेकोरेटर के साथ है।

यहाँ छवि विवरण दर्ज करें

inspectतुलना के लिए मॉड्यूल का उपयोग करने के बाद यहां एक और अंतर है ।

यहाँ छवि विवरण दर्ज करें

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