Skip to content
This page has been auto-translated and may contain errors.View in English

क्लासेस और ऑब्जेक्ट्स

अब तक आपने जिन भी टाइप्स के साथ काम किया है (स्ट्रिंग्स, लिस्ट्स, डिक्शनरीज़) वे वास्तव में क्लासेस हैं। जब आप "hello".upper() कॉल करते हैं, तो आप एक स्ट्रिंग ऑब्जेक्ट पर एक मेथड कॉल कर रहे होते हैं। क्लासेस आपको अपने स्वयं के डेटा और व्यवहार के साथ अपने स्वयं के टाइप्स परिभाषित करने देती हैं। एक Player क्लास एक नाम, स्कोर और लेवल रख सकती है, और जान सकती है कि स्वयं को कैसे प्रदर्शित करना है।

क्लासेस यूज़र-डिफ़ाइंड टाइप्स के लिए तंत्र हैं। एक क्लास एक टेम्पलेट परिभाषित करती है: डेटा जो प्रत्येक इंस्टेंस रखता है (एट्रिब्यूट्स) और जिन ऑपरेशंस का यह समर्थन करता है (मेथड्स)। समानांतर वैरिएबल्स में मानों को ट्रैक करने और उन्हें हर जगह पास करने के बजाय, आप उन्हें एक स्पष्ट इंटरफ़ेस वाले एक सुसंगठित ऑब्जेक्ट में बंडल करते हैं।

एक class स्टेटमेंट वर्तमान नेमस्पेस में एक नया टाइप ऑब्जेक्ट बनाता है, इसे बनाने के लिए मेटाक्लास (डिफ़ॉल्ट type) का उपयोग करता है। इंस्टेंसेस क्लास को कॉल करके बनाए जाते हैं, जो ऑब्जेक्ट को आवंटित करने के लिए __new__ और इसे आरंभीकृत करने के लिए __init__ को आह्वान करता है। Python का ऑब्जेक्ट मॉडल एकसमान है: सब कुछ एक ऑब्जेक्ट है, जिसमें क्लासेस, फ़ंक्शंस और स्वयं टाइप्स शामिल हैं।

ब्लूप्रिंट और इंस्टेंसेस

एक क्लास एक ब्लूप्रिंट है। एक इंस्टेंस उस ब्लूप्रिंट से बनी एक विशिष्ट चीज़ है। आप जितने चाहें उतने इंस्टेंस बना सकते हैं, प्रत्येक के अपने डेटा के साथ लेकिन क्लास में परिभाषित समान मेथड्स को साझा करते हुए।

एक क्लास संरचना और व्यवहार को परिभाषित करती है। इंस्टेंसेस उस क्लास से बनाए गए ऑब्जेक्ट्स हैं; प्रत्येक का अपना एट्रिब्यूट नेमस्पेस होता है लेकिन क्लास के मेथड ऑब्जेक्ट्स को साझा करता है। एक इंस्टेंस बनाना क्लास को एक फ़ंक्शन की तरह कॉल करता है: Dog() एक नया Dog इंस्टेंस बनाता है।

एक क्लास को कॉल करना type.__call__ को आह्वान करता है, जो इंस्टेंस आवंटित करने के लिए cls.__new__(cls) और इसे आरंभीकृत करने के लिए cls.__init__(instance, ...) कॉल करता है। परिणामी ऑब्जेक्ट का __class__ क्लास की ओर वापस इंगित करता है। मेथड लुकअप MRO का अनुसरण करता है: इंस्टेंस __dict__, क्लास __dict__, और फिर MRO श्रृंखला।

python
class Dog:
    def bark(self):
        print("Woof!")

rex  = Dog()
luna = Dog()

rex.bark()    # "Woof!"
luna.bark()   # "Woof!"

Dog क्लास है। rex और luna इंस्टेंसेस हैं: दो अलग-अलग कुत्ते, प्रत्येक क्लास में परिभाषित समान व्यवहार साझा करते हुए।

__init__ और self

__init__ वह मेथड है जिसे Python तब स्वचालित रूप से कॉल करता है जब आप एक नया इंस्टेंस बनाते हैं। यह वह जगह है जहाँ आप ऑब्जेक्ट के लिए शुरुआती डेटा सेट करते हैं। self वह तरीका है जिससे एक मेथड उस विशिष्ट इंस्टेंस को संदर्भित करता है जिस पर वह कार्य कर रहा है, और यह हमेशा पहला पैरामीटर होता है।

__init__ एक नए आवंटित इंस्टेंस को आरंभीकृत करता है। self हर इंस्टेंस मेथड के पहले पैरामीटर के लिए एक पारंपरिक नाम है; जब आप alice.display() कॉल करते हैं तो Python स्वचालित रूप से इंस्टेंस पास करता है। __init__ के अंदर self पर सेट किए गए एट्रिब्यूट्स इंस्टेंस एट्रिब्यूट्स हैं: प्रत्येक इंस्टेंस की अपनी कॉपी होती है।

__init__ __new__ से पहले से आवंटित इंस्टेंस को प्राप्त करता है और इसे सेट अप करता है। self कोई कीवर्ड नहीं है; यह पहला पोज़िशनल पैरामीटर है, जो डिस्क्रिप्टर प्रोटोकॉल द्वारा पास किया जाता है जब आप instance.method() कॉल करते हैं। self.attr = value सेट करना object.__setattr__ कॉल करता है, जो इंस्टेंस के __dict__ में लिखता है। __init__ में सेट नहीं किए गए एट्रिब्यूट्स अभी भी बाद में किसी भी इंस्टेंस पर सेट किए जा सकते हैं, क्योंकि Python डिक्शनरीज़ डायनेमिक हैं।

python
class Player:
    def __init__(self, name, score=0):
        self.name  = name
        self.score = score

    def add_points(self, points):
        self.score += points

    def display(self):
        print(f"{self.name}: {self.score} points")

alice = Player("आरव")
bob   = Player("दीया", score=50)

alice.add_points(30)
alice.display()   # "आरव: 30 points"
bob.display()     # "दीया: 50 points"

self.name और self.score इंस्टेंस एट्रिब्यूट्स हैं: वे विशिष्ट ऑब्जेक्ट से संबंधित हैं, स्वयं क्लास से नहीं। प्रत्येक Player इंस्टेंस का अपना name और score होता है।

मेथड्स

एक क्लास के अंदर परिभाषित कोई भी फ़ंक्शन एक मेथड है। इंस्टेंस मेथड्स में हमेशा पहले पैरामीटर के रूप में self होता है; Python इसे स्वचालित रूप से पास करता है। मेथड्स self के माध्यम से इंस्टेंस के डेटा को पढ़ और बदल सकते हैं।

इंस्टेंस मेथड्स नियमित फ़ंक्शंस हैं जो क्लास नेमस्पेस में संग्रहीत होते हैं। डिस्क्रिप्टर प्रोटोकॉल instance.method को एक बाउंड मेथड में बदल देता है, स्वचालित रूप से self को बाइंड करता है। एक मेथड से self लौटाना मेथड चेनिंग को सक्षम करता है: obj.scale(2).rotate(90)

instance.method डिस्क्रिप्टर लुकअप ट्रिगर करता है: type.__getattribute__ क्लास __dict__ में method ढूँढता है, इसे एक फ़ंक्शन के रूप में पहचानता है (जो डिस्क्रिप्टर प्रोटोकॉल लागू करता है), और एक बाउंड मेथड ऑब्जेक्ट लौटाता है जो इंस्टेंस के साथ पहले से लागू फ़ंक्शन को रैप करता है। यह वह तरीका है जिससे self स्वचालित रूप से पास होता है। self लौटाना फ़्लूएंट इंटरफ़ेसेस को सक्षम करता है।

python
class Circle:
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius ** 2

    def scale(self, factor):
        self.radius *= factor
        return self    # self लौटाने से चेनिंग की अनुमति मिलती है: c.scale(2).scale(0.5)

c = Circle(5)
print(c.area())    # 78.53975
c.scale(2)
print(c.area())    # 314.159

क्लास वैरिएबल्स बनाम इंस्टेंस वैरिएबल्स

सीधे क्लास पर परिभाषित वैरिएबल्स (न कि __init__ के अंदर) क्लास वैरिएबल्स हैं। सभी इंस्टेंसेस एक ही क्लास वैरिएबल साझा करते हैं। __init__ के अंदर self पर सेट किए गए वैरिएबल्स इंस्टेंस वैरिएबल्स हैं, जो प्रत्येक ऑब्जेक्ट के लिए अनूठे हैं।

क्लास वैरिएबल्स क्लास __dict__ में रहते हैं और सभी इंस्टेंसेस द्वारा साझा किए जाते हैं। इंस्टेंस वैरिएबल्स प्रत्येक इंस्टेंस के __dict__ में रहते हैं। जब आप self.attr पढ़ते हैं, तो Python पहले इंस्टेंस की जाँच करता है, फिर क्लास की। जब आप self.attr = value लिखते हैं, तो यह हमेशा इंस्टेंस एट्रिब्यूट बनाता या अपडेट करता है, यदि एक मौजूद हो तो क्लास वैरिएबल को छाया कर देता है।

एक इंस्टेंस पर एट्रिब्यूट लुकअप इस क्रम का अनुसरण करता है: इंस्टेंस __dict__, क्लास __dict__, और फिर MRO। self.attr पर लिखना हमेशा इंस्टेंस __dict__ को लक्षित करता है (जब तक __setattr__ ओवरराइड न हो)। एक क्लास वैरिएबल तब तक साझा होता है जब तक self.attr पर एक राइट इंस्टेंस छाया नहीं बनाता। म्यूटेबल क्लास वैरिएबल्स (जैसे लिस्ट्स) विशेष रूप से पेचीदा हैं: सभी इंस्टेंसेस समान ऑब्जेक्ट साझा करते हैं, इसलिए किसी भी इंस्टेंस के माध्यम से म्यूटेशन सभी को दिखाई देता है।

python
class Player:
    max_lives = 3    # क्लास वैरिएबल, हर Player के लिए समान

    def __init__(self, name):
        self.name  = name   # इंस्टेंस वैरिएबल, प्रत्येक Player के लिए अनूठा
        self.lives = Player.max_lives

    def die(self):
        self.lives -= 1

alice = Player("आरव")
bob   = Player("दीया")

Player.max_lives = 5    # सभी वर्तमान और भविष्य के इंस्टेंसेस के लिए बदलें

सभी इंस्टेंसेस में साझा मानों के लिए क्लास वैरिएबल्स का उपयोग करें: कॉन्स्टैंट्स, काउंटर्स, डिफ़ॉल्ट्स। उस डेटा के लिए इंस्टेंस वैरिएबल्स का उपयोग करें जो प्रति ऑब्जेक्ट भिन्न होता है।

__str__ और __repr__

__str__ नियंत्रित करता है कि print() और f-स्ट्रिंग्स आपके ऑब्जेक्ट के लिए क्या दिखाते हैं। __repr__ कंसोल में और डिबगिंग के लिए दिखाए जाने वाले डेवलपर व्यू को नियंत्रित करता है। हमेशा __repr__ परिभाषित करें। __str__ तब परिभाषित करें जब आप डिबग व्यू से अलग एक स्वच्छ यूज़र-फेसिंग प्रदर्शन चाहते हैं।

__str__ str(), print() और f-स्ट्रिंग्स द्वारा कॉल किया जाता है। __repr__ repr() द्वारा कॉल किया जाता है और REPL में दिखाया जाता है। यदि केवल __repr__ परिभाषित है, तो Python इसे दोनों के लिए उपयोग करता है। परंपरा: __repr__ को एक ऐसी स्ट्रिंग लौटानी चाहिए जो ऑब्जेक्ट को पुनर्निर्माण कर सके; __str__ को एक मानव-पठनीय सारांश लौटाना चाहिए।

str(obj) type(obj).__str__(obj) कॉल करता है, यदि __str__ परिभाषित नहीं है तो __repr__ पर फ़ॉलबैक करता है। repr(obj) type(obj).__repr__(obj) कॉल करता है। f-स्ट्रिंग्स के अंदर, {obj} __format__ कॉल करता है, जो डिफ़ॉल्ट रूप से __str__ कॉल करता है। {obj!r} फ़ॉर्मेटिंग से पहले repr() लागू करता है। __repr__ परंपरा: ClassName(arg1=..., arg2=...) रूप की एक स्ट्रिंग लौटाएँ जो, जब मूल्यांकित की जाए, तो ऑब्जेक्ट को पुनरुत्पादित करेगी।

python
class Player:
    def __init__(self, name, score):
        self.name  = name
        self.score = score

    def __str__(self):
        return f"{self.name} ({self.score} pts)"

    def __repr__(self):
        return f"Player(name={self.name!r}, score={self.score})"

alice = Player("आरव", 87)
print(alice)        # "आरव (87 pts)"   (__str__ का उपयोग करता है)
repr(alice)         # "Player(name='आरव', score=87)"  (__repr__ का उपयोग करता है)

हमेशा __repr__ परिभाषित करें। __str__ तब परिभाषित करें जब आप डिबग व्यू से अलग एक स्वच्छ यूज़र-फेसिंग प्रतिनिधित्व चाहते हैं। यदि केवल __repr__ परिभाषित है, तो Python इसे दोनों के लिए उपयोग करता है।

प्राइवेट परंपरा

Python में वास्तव में प्राइवेट वैरिएबल्स नहीं हैं, लेकिन एक नाम की शुरुआत में एक एकल अंडरस्कोर (_balance) एक परंपरा है जो संकेत देता है "यह आंतरिक है, इसे क्लास के बाहर से सीधे उपयोग न करें"। यह भाषा द्वारा लागू नहीं किया जाता है; यह अन्य डेवलपर्स के लिए एक संचार है।

एक एकल अंडरस्कोर (_attr) आंतरिक उपयोग का संकेत देने वाली एक परंपरा है। Python इसे लागू नहीं करता है, लेकिन सभी लिंटर्स, IDE और डेवलपर्स इसका सम्मान करते हैं। एक दोहरा अंडरस्कोर (__attr) नेम मैंगलिंग ट्रिगर करता है: Python इसे _ClassName__attr के रूप में फिर से लिखता है, जो सबक्लासेस में आकस्मिक टकराव को रोकता है। यह सच्ची गोपनीयता नहीं है; यह एक टकराव-निवारण तंत्र है।

एकल अंडरस्कोर: केवल परंपरा, कोई प्रवर्तन नहीं। दोहरा अंडरस्कोर: नेम मैंगलिंग __attr को कंपाइल समय पर _ClassName__attr में बदल देता है, रनटाइम पर नहीं। यह एक सबक्लास टकराव गार्ड है, गोपनीयता तंत्र नहीं; मैंगल किया गया नाम अभी भी बाहरी रूप से सुलभ है। __slots__ एक अधिक मौलिक नियंत्रण है: यह इंस्टेंस __dict__ को स्लॉट डिस्क्रिप्टर्स के एक निश्चित सेट से बदल देता है, मनमाने एट्रिब्यूट निर्माण को रोकता है और मेमोरी ओवरहेड को कम करता है।

python
class BankAccount:
    def __init__(self, balance):
        self._balance = balance    # _ का अर्थ है "हाथ हटाओ"

    def deposit(self, amount):
        if amount > 0:
            self._balance += amount

    def balance(self):
        return self._balance

एक दोहरा अंडरस्कोर (__name) नेम मैंगलिंग ट्रिगर करता है; Python सबक्लासेस में टकराव से बचने के लिए एट्रिब्यूट का नाम बदलकर _ClassName__name कर देता है। इसकी शायद ही कभी आवश्यकता होती है। एकल अंडरस्कोर अधिकांश कोड में परंपरा है।

इनहेरिटेंस

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

इनहेरिटेंस एक "is-a" संबंध बनाता है। सबक्लास पैरेंट के सभी मेथड्स और एट्रिब्यूट्स को इनहेरिट करता है और उनमें से किसी को भी ओवरराइड कर सकता है। मेथड रिज़ॉल्यूशन ऑर्डर (MRO) एट्रिब्यूट्स के लिए लुकअप अनुक्रम को परिभाषित करता है। Python मल्टीपल इनहेरिटेंस का समर्थन करता है; MRO की गणना C3 लीनियराइज़ेशन एल्गोरिथम के साथ की जाती है।

MRO की गणना C3 लीनियराइज़ेशन का उपयोग करके type.__mro_entries__ द्वारा की जाती है, जो ClassName.__mro__ के रूप में सुलभ है। मेथड रिज़ॉल्यूशन MRO का बाएँ से दाएँ अनुसरण करता है। isinstance(obj, cls) जाँचने के लिए MRO पर चलता है। मल्टीपल इनहेरिटेंस शक्तिशाली है लेकिन डायमंड इनहेरिटेंस बना सकता है; MRO इसे निर्धारित ढंग से हल करता है। super() MRO का अनुसरण करता है, केवल प्रत्यक्ष पैरेंट क्लास का नहीं।

python
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

pets = [Dog("रेक्स"), Cat("लूना"), Dog("मैक्स")]
for pet in pets:
    print(pet.speak())

Dog और Cat Animal से __init__ इनहेरिट करते हैं, इसलिए उन्हें अपने स्वयं की आवश्यकता नहीं है। वे अपने विशिष्ट व्यवहार के साथ speak() को ओवरराइड करते हैं।

super()

super() पैरेंट क्लास से एक मेथड कॉल करता है। इसका उपयोग तब करें जब आप पैरेंट के व्यवहार को पूरी तरह से बदलने के बजाय विस्तारित करना चाहते हैं: इसके सेटअप को चलाने के लिए पैरेंट के __init__ को कॉल करें, फिर अपने सबक्लास को जो कुछ भी ऊपर चाहिए वह जोड़ें।

super() एक प्रॉक्सी ऑब्जेक्ट लौटाता है जो MRO में अगली क्लास को मेथड कॉल्स सौंपता है। जब पैरेंट के पास एक होता है तो हमेशा सबक्लास __init__ से super().__init__() कॉल करें। इसे छोड़ने का अर्थ है कि पैरेंट का सेटअप कोड नहीं चलता, जो ऑब्जेक्ट को टूटी हुई अवस्था में छोड़ सकता है।

बिना किसी आर्ग्युमेंट के super() (Python 3) MRO स्थिति निर्धारित करने के लिए __class__ (एक कंपाइलर-इंजेक्टेड सेल वैरिएबल) और संलग्न मेथड के पहले आर्ग्युमेंट का उपयोग करता है। super(cls, self) स्पष्ट रूप है। प्रॉक्सी cls के बाद type(self).__mro__ में अगली क्लास से शुरू करके मेथड लुकअप हल करता है। यह सही मल्टीपल इनहेरिटेंस के लिए आवश्यक है: super() प्रत्येक क्लास के __init__ को पूर्ण MRO के माध्यम से चेन करता है।

python
class Animal:
    def __init__(self, name, sound):
        self.name  = name
        self.sound = sound

class Dog(Animal):
    def __init__(self, name):
        super().__init__(name, "Woof")   # Animal.__init__ कॉल करें
        self.tricks = []                  # कुछ अतिरिक्त जोड़ें

    def learn(self, trick):
        self.tricks.append(trick)

rex = Dog("रेक्स")
rex.learn("sit")
print(rex.tricks)   # ["sit"]

हमेशा super().__init__() कॉल करें जब आपके सबक्लास का अपना __init__ हो और पैरेंट का भी हो।

क्लास मेथड्स और स्टैटिक मेथड्स

@classmethod एक ऐसा मेथड बनाता है जो एक इंस्टेंस के बजाय स्वयं क्लास को प्राप्त करता है। यह वैकल्पिक कंस्ट्रक्टर्स के लिए उपयोगी है: स्ट्रिंग, फ़ाइल या किसी अन्य प्रारूप से एक इंस्टेंस बनाना। @staticmethod एक साधारण फ़ंक्शन है जो संगठनात्मक कारणों से क्लास के अंदर रहता है; यह न तो इंस्टेंस और न ही क्लास को प्राप्त करता है।

@classmethod अपने पहले आर्ग्युमेंट के रूप में cls (क्लास) प्राप्त करता है, न कि एक इंस्टेंस। प्राथमिक उपयोग वैकल्पिक कंस्ट्रक्टर्स हैं जो विभिन्न इनपुट प्रारूपों से इंस्टेंसेस बनाते हैं। @staticmethod क्लास के तहत नेमस्पेस किया गया एक नियमित फ़ंक्शन है; इसकी क्लास या इंस्टेंस तक कोई पहुँच नहीं है। कंस्ट्रक्टर्स के लिए @classmethod, क्लास से तार्किक रूप से बंधे यूटिलिटी फ़ंक्शंस के लिए @staticmethod का उपयोग करें।

@classmethod डिस्क्रिप्टर प्रोटोकॉल का उपयोग करता है: classmethod डिस्क्रिप्टर एक बाउंड मेथड लौटाता है जहाँ पहला आर्ग्युमेंट क्लास है, इंस्टेंस नहीं। cls कॉल समय पर वास्तविक क्लास है, इसलिए सबक्लास मेथड्स को सबक्लास मिलता है। @staticmethod एक सरल डिस्क्रिप्टर है जो अंतर्निहित फ़ंक्शन को अपरिवर्तित लौटाता है, बिना किसी अंतर्निहित पहले आर्ग्युमेंट के। दोनों डेकोरेटर्स डिस्क्रिप्टर व्यवहार को संशोधित करते हैं; कोई भी फ़ंक्शन के कोड ऑब्जेक्ट को प्रभावित नहीं करता है।

python
class Player:
    def __init__(self, name, score):
        self.name  = name
        self.score = score

    @classmethod
    def from_string(cls, data):
        name, score = data.split(",")
        return cls(name, int(score))

alice = Player.from_string("आरव,87")
python
class Player:
    @staticmethod
    def is_valid_name(name):
        return name.isalpha() and len(name) >= 2

Player.is_valid_name("आरव")   # True
Player.is_valid_name("A1")      # False

वैकल्पिक कंस्ट्रक्टर्स के लिए @classmethod का उपयोग करें। यूटिलिटी फ़ंक्शंस के लिए @staticmethod का उपयोग करें जो तार्किक रूप से क्लास के साथ हैं लेकिन इंस्टेंस या क्लास डेटा की आवश्यकता नहीं है।

@property

@property आपको एक मेथड को एक एट्रिब्यूट की तरह एक्सेस करने देता है, बिना कोष्ठक की आवश्यकता के। उन मानों के लिए इसका उपयोग करें जो अन्य एट्रिब्यूट्स से गणना किए जाते हैं और सरल एट्रिब्यूट एक्सेस के रूप में पढ़ने में स्वाभाविक लगते हैं।

@property एक मेथड को रीड-ओनली एट्रिब्यूट में बदल देता है। एट्रिब्यूट एक्सेस होने पर मेथड चलता है। यह संग्रहीत डेटा से प्राप्त गणना किए गए मानों के लिए, और सार्वजनिक इंटरफ़ेस को बदले बिना एट्रिब्यूट एक्सेस में सत्यापन जोड़ने के लिए उपयोगी है। एक युग्मित @name.setter एट्रिब्यूट को लिखने योग्य बनाता है।

@property एक डिस्क्रिप्टर है: property(fget, fset, fdel, doc)instance.attr एक्सेस करना fget(instance) कॉल करता है। @name.setter fset सेट करता है। @name.deleter fdel सेट करता है। प्रॉपर्टीज़ क्लास पर लुकअप की जाती हैं, इंस्टेंस पर नहीं, इसीलिए instance.__dict__[name] एक प्रॉपर्टी को छाया नहीं देता (डेटा डिस्क्रिप्टर्स इंस्टेंस __dict__ पर प्राथमिकता लेते हैं)। प्रॉपर्टीज़ Java-शैली getter/setter मेथड्स का Pythonic विकल्प हैं।

python
class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return 3.14159 * self.radius ** 2

    @property
    def diameter(self):
        return self.radius * 2

c = Circle(5)
print(c.area)      # 78.53975 (एक एट्रिब्यूट की तरह दिखता है, एक मेथड की तरह चलता है)
print(c.diameter)  # 10

प्रॉपर्टीज़ गणना किए गए मानों के लिए उपयोगी हैं: अन्य एट्रिब्यूट्स से प्राप्त चीज़ें जिन्हें () के बिना एक्सेस करना स्वाभाविक लगता है।

व्यवहार में

इंस्टेंस एट्रिब्यूट्स, मेथड्स, एक @property, और __str__ के साथ एक Player क्लास:

python
class Player:
    max_lives = 3

    def __init__(self, name: str):
        self.name  = name
        self.score = 0
        self.lives = Player.max_lives

    def earn_points(self, amount: int) -> None:
        self.score += amount

    def take_hit(self) -> bool:
        self.lives -= 1
        return self.lives > 0

    @property
    def is_alive(self) -> bool:
        return self.lives > 0

    def __str__(self) -> str:
        return f"{self.name} | Score: {self.score} | Lives: {self.lives}"

alice = Player("आरव")
alice.earn_points(50)
alice.take_hit()
print(alice)            # "आरव | Score: 50 | Lives: 2"
print(alice.is_alive)   # True

एक User क्लास जो एक @property गेटर के साथ एक प्राइवेट एट्रिब्यूट, एक deactivate मेथड, और एक to_dict सीरियलाइज़र का उपयोग करती है:

python
class User:
    def __init__(self, user_id: int, username: str, email: str):
        self.id       = user_id
        self.username = username
        self.email    = email
        self._active  = True

    @property
    def active(self) -> bool:
        return self._active

    def deactivate(self) -> None:
        self._active = False

    def to_dict(self) -> dict:
        return {
            "id":       self.id,
            "username": self.username,
            "email":    self.email,
            "active":   self._active,
        }

    def __repr__(self) -> str:
        return f"User(id={self.id}, username={self.username!r})"

alice = User(1, "आरव", "[email protected]")
print(alice.to_dict())
alice.deactivate()
print(alice.active)   # False

एक DataSplit क्लास जो प्रॉपर्टीज़ के पीछे ट्रेन/वैलिडेशन स्लाइसिंग को संक्षिप्त करती है, स्वच्छ डिबग आउटपुट के लिए __repr__ के साथ:

python
class DataSplit:
    def __init__(self, data: list, train_ratio: float = 0.8):
        split       = int(len(data) * train_ratio)
        self._train = data[:split]
        self._val   = data[split:]

    @property
    def train(self) -> list:
        return self._train

    @property
    def val(self) -> list:
        return self._val

    @property
    def sizes(self) -> tuple[int, int]:
        return len(self._train), len(self._val)

    def __repr__(self) -> str:
        return f"DataSplit(train={len(self._train)}, val={len(self._val)})"

data  = list(range(100))
split = DataSplit(data, train_ratio=0.8)
print(split)         # DataSplit(train=80, val=20)
print(split.sizes)   # (80, 20)

_train और _val पर अंडरस्कोर उपसर्ग संकेत देता है कि कॉलर्स को कच्ची लिस्ट्स को सीधे म्यूटेट करने के बजाय प्रॉपर्टीज़ के माध्यम से जाना चाहिए। Python इसे लागू नहीं करेगा, लेकिन यह एक स्पष्ट अनुबंध स्थापित करता है।