Kodokon kodokon.com

प्रोटोकॉल और विशेष मेथड: __repr__, __eq__, कॉन्टेक्स्ट मैनेजर

Python के ऑब्जेक्ट मॉडल में महारत हासिल करें: __eq__/__hash__ का अनुबंध, NotImplemented वैल्यू, कॉन्टेक्स्ट मैनेजर और Protocol के साथ स्ट्रक्चरल टाइपिंग।

9 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

Python में, हर ऑपरेटर, हर रूपांतरण, हर with कथन किसी विशेष मेथड से होकर गुज़रता है: a == b a.__eq__(b) को कॉल करता है, len(x) x.__len__() को कॉल करता है। __repr__ - डेवलपर के लिए बनी असंदिग्ध प्रस्तुति, जो डीबगर में और लिस्ट के भीतर दिखती है - को __str__ से अलग करना ध्यान रखें, जो अंतिम उपयोगकर्ता के लिए है। यदि __str__ अनुपस्थित हो, तो Python __repr__ पर लौट आता है: इसलिए हमेशा पहले __repr__ लागू करें, आदर्श रूप से ऐसे रूप में जिससे ऑब्जेक्ट को दोबारा बनाया जा सके।

PYTHON
class Money:
    def __init__(self, amount: int, currency: str):
        self.amount = amount
        self.currency = currency

    def __repr__(self) -> str:
        return f"Money({self.amount!r}, {self.currency!r})"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Money):
            return NotImplemented
        return (self.amount, self.currency) == (
            other.amount, other.currency)

print(Money(10, "EUR") == Money(10, "EUR"))
print(Money(10, "EUR") == "10 EUR")
किसी अज्ञात टाइप से तुलना NotImplemented लौटाती है, कभी False नहीं।

NotImplemented लौटाना (वह वैल्यू, जिसे NotImplementedError अपवाद से भ्रमित न करें) ऑपरेटर वार्ता प्रोटोकॉल को सक्रिय करता है: Python तब उलटी क्रिया other.__eq__(self) आज़माता है, और अंतिम उपाय के रूप में ही False निष्कर्ष निकालता है। सीधे False लौटाना उस वार्ता को छोटा कर देता और सबक्लासों या तीसरे पक्ष के उन टाइप के साथ तुलना तोड़ देता जो असल में उत्तर देना जानते होते।

PYTHON
import time

class Timer:
    def __enter__(self):
        self.start = time.perf_counter()
        return self

    def __exit__(self, exc_type, exc, tb):
        stop = time.perf_counter()
        self.elapsed = stop - self.start
        return False

with Timer() as timer:
    sum(range(1_000_000))
print(f"{timer.elapsed:.4f} s")
False लौटाने से कोई भी अपवाद सामान्य रूप से आगे बढ़ता रहता है।

यदि __exit__ True लौटाता है, तो वर्तमान अपवाद दबा दिया जाता है - contextlib.suppress ठीक इसी तरह काम करता है। पूरी क्लास लिखने से बचने के लिए, @contextlib.contextmanager किसी जनरेटर को कॉन्टेक्स्ट मैनेजर में बदल देता है: yield से पहले का कोड __enter__ की भूमिका निभाता है, उसके बाद का कोड (आदर्श रूप से किसी finally में) __exit__ की। ऑब्जेक्ट मॉडल का एक और स्तंभ: typing मॉड्यूल के Protocol टाइप डक टाइपिंग को औपचारिक रूप देते हैं - कोई क्लास तब संगत होती है जब वह सही मेथड उजागर करती है, बिना किसी घोषित विरासत के।

PYTHON
from typing import Protocol, runtime_checkable

@runtime_checkable
class Closable(Protocol):
    def close(self) -> None: ...

class Session:
    def close(self) -> None:
        print("session closed")

def shutdown(resource: Closable) -> None:
    resource.close()

shutdown(Session())
print(isinstance(Session(), Closable))
Session कोई विरासत घोषित नहीं करता: अनुरूपता स्ट्रक्चरल है।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. __eq__ में, जब आपका सामना ऐसे टाइप से हो जिससे आप तुलना नहीं कर सकते, तो क्या लौटाना चाहिए?
    • False, ताकि असमानता स्पष्ट रूप से बताई जा सके
    • NotImplementedError उठाएँ
    • NotImplemented, ताकि Python दूसरे ऑपरैंड की उलटी क्रिया आज़मा सके
    • None, जिसे Python False में बदल देगा
  2. आप किसी क्लास में __hash__ को छुए बिना __eq__ जोड़ते हैं। इसका परिणाम क्या है?
    • कोई नहीं: __hash__ object से विरासत में मिला रहता है
    • __hash__ None पर सेट हो जाता है: इंस्टेंस अब dict की कुंजी या set के तत्व के रूप में इस्तेमाल नहीं हो सकते
    • हैश अपने आप एट्रिब्यूट के टपल का हैश बन जाता है
  3. True लौटाने वाले __exit__ मेथड का क्या अर्थ है?
    • with ब्लॉक बिना त्रुटि के समाप्त हुआ
    • कॉन्टेक्स्ट मैनेजर दोबारा इस्तेमाल किया जा सकता है
    • Python with ब्लॉक को दूसरी बार चलाएगा
    • वर्तमान अपवाद दबा दिया जाता है और निष्पादन with ब्लॉक के बाद से जारी रहता है