कक्षाएँ और वस्तुएँ

आप अब तक जो भी प्रकार के साथ काम कर चुके हैं (strings, lists, dictionaries) वास्तव में एक कक्षा है। जब आप "hello".upper() को कॉल करते हैं, तो आप एक string ऑब्जेक्ट पर एक विधि को कॉल कर रहे हैं। कक्षाएँ आपको अपने स्वयं के प्रकार को परिभाषित करने देती हैं जिनके अपने डेटा और व्यवहार हों। एक Player कक्षा एक नाम, एक स्कोर, और एक स्तर रख सकती है, और जानती है कि अपने आप को कैसे प्रदर्शित करना है।
Blueprint और instances
एक कक्षा एक blueprint है। एक instance उस blueprint से बनी एक विशिष्ट चीज़ है। आप जितने चाहें उतने instances बना सकते हैं, प्रत्येक के अपने डेटा के साथ लेकिन कक्षा में परिभाषित विधियों को साझा करते हुए।
class Dog:
def bark(self):
print("Woof!")
rex = Dog()
luna = Dog()
rex.bark() # "Woof!"
luna.bark() # "Woof!"Dog कक्षा है। rex और luna instances हैं: दो अलग-अलग कुत्ते, प्रत्येक कक्षा में परिभाषित एक ही व्यवहार को साझा करते हैं।
Dog(), और आपको एक ताज़ा instance वापस मिलता है। हर instance कक्षा की methods को साझा करता है लेकिन अपना डेटा रखता है, इसलिए rex और luna एक जैसा व्यवहार कर सकते हैं जबकि अलग-अलग कुत्ते होते हैं। __init__ और self
__init__ method है जिसे Python स्वचालित रूप से कॉल करता है जब आप एक नया instance बनाते हैं। यह वह जगह है जहाँ आप ऑब्जेक्ट के लिए शुरुआती डेटा सेट करते हैं। self यह है कि एक method अपने को संदर्भित करता है जिस विशिष्ट instance पर काम कर रहा है, और यह हमेशा पहला parameter है।
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("Alice")
bob = Player("Bob", score=50)
alice.add_points(30)
alice.display() # "Alice: 30 points"
bob.display() # "Bob: 50 points"self.name और self.score instance attributes हैं: वे विशिष्ट ऑब्जेक्ट से संबंधित हैं, कक्षा से नहीं। प्रत्येक Player instance के पास अपना name और score है।
__init__ उस पल चलता है जब आप एक instance बनाते हैं, इसलिए यह वह जगह है जहाँ आप self.name = value के साथ शुरुआती डेटा सेट करते हैं। self वह instance है जिस पर Python काम कर रहा है, और यह हमेशा एक method का पहला parameter है, आपको स्वचालित रूप से सौंपा गया। आप कभी alice.display() को कॉल करते समय self को स्वयं पास नहीं करते हैं। Methods
कोई भी function एक कक्षा के अंदर परिभाषित एक method है। Instance methods हमेशा self को पहले parameter के रूप में रखते हैं; Python इसे स्वचालित रूप से पास करता है। Methods instance के डेटा को self के माध्यम से पढ़ और बदल सकते हैं।
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 # returning self allows chaining: c.scale(2).scale(0.5)
c = Circle(5)
print(c.area()) # 78.53975
c.scale(2)
print(c.area()) # 314.159self है, जिस instance पर यह काम कर रहा है। Python self को आपके लिए पास करता है, इसलिए आप c.area() को कुछ भी extra के साथ कॉल करते हैं। self के माध्यम से एक method उस ऑब्जेक्ट के अपने डेटा को पढ़ और बदलता है। Class variables बनाम instance variables
Variables सीधे कक्षा पर परिभाषित (__init__ के अंदर नहीं) class variables हैं। सभी instances एक ही class variable को साझा करते हैं। Variables self पर __init__ के अंदर सेट किए गए instance variables हैं, प्रत्येक ऑब्जेक्ट के लिए अनोखे।
class Player:
max_lives = 3 # class variable, same for every Player
def __init__(self, name):
self.name = name # instance variable, unique to each Player
self.lives = Player.max_lives
def die(self):
self.lives -= 1
alice = Player("Alice")
bob = Player("Bob")
Player.max_lives = 5 # change for all current and future instancesसभी instances के पार shared values के लिए class variables use करें: constants, counters, defaults। Data के लिए instance variables use करें जो प्रति ऑब्जेक्ट अलग होता है।
self.attr = value को लिखना हमेशा instance की own copy को बनाता या update करता है। इसलिए class variables के लिए reach करें जब एक value सभी के लिए एक ही हो, instance variables जब यह प्रति ऑब्जेक्ट अलग हो। __str__ और __repr__
__str__ नियंत्रित करता है कि print() और f-strings आपकी ऑब्जेक्ट के लिए क्या दिखाते हैं। __repr__ नियंत्रित करता है console में दिखाया गया developer view और debugging के लिए। हमेशा __repr__ को define करें। __str__ को define करें जब आप एक साफ user-facing display चाहते हैं जो debug view से अलग हो।
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("Alice", 87)
print(alice) # "Alice (87 pts)" (uses __str__)
repr(alice) # "Player(name='Alice', score=87)" (uses __repr__)हमेशा __repr__ को define करें। __str__ को define करें जब आप एक साफ user-facing representation चाहते हैं जो debug view से अलग हो। अगर केवल __repr__ को define किया जाता है, Python इसे दोनों के लिए use करता है।
__str__ यह है जो print() और f-strings दिखाते हैं, friendly version। __repr__ developer view है जो आप console में देखते हैं। हमेशा __repr__ को लिखें; यह वह है जिसके पास job है यहाँ तक कि जब आप __str__ को भूल जाएं। __str__ को केवल तभी add करें जब user-facing text अलग तरीके से पढ़ना चाहिए। Private convention
Python के पास कोई real private variables नहीं हैं, लेकिन एक नाम के शुरु में एक underscore (_balance) एक convention है जो signal करता है "यह आंतरिक है, इसे सीधे कक्षा के बाहर from use न करें"। यह language द्वारा enforce नहीं है; यह अन्य developers को एक communication है।
class BankAccount:
def __init__(self, balance):
self._balance = balance # _ means "hands off"
def deposit(self, amount):
if amount > 0:
self._balance += amount
def balance(self):
return self._balanceएक double underscore (__name) name mangling को trigger करता है; Python attribute को _ClassName__name में rename करता है subclasses में conflicts को avoid करने के लिए। यह rarely needed है। Single underscore अधिकांश code में convention है।
_balance) "internal, इससे बाहर रहें" के लिए agreed signal है। कुछ भी आपको अंदर reach करने से नहीं रोकता, यह अन्य developers को एक message है, जिसमें future you भी शामिल है। Double underscore subclasses में name clashes को avoid करने के लिए एक rarer tool है; single underscore वह है जिसे आप दिन में रोज़ use करेंगे। Inheritance
एक कक्षा दूसरी कक्षा से inherit कर सकती है, सभी attributes और methods को स्वचालित रूप से प्राप्त कर सकती है। आप फिर subclass में specific methods को override कर सकते हैं उनके behaviour को change करने के लिए। यह आपको एक common base को reuse करने और जहाँ need हो वहाँ specialise करने देता है।
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("Rex"), Cat("Luna"), Dog("Max")]
for pet in pets:
print(pet.speak())Dog और Cat Animal से __init__ को inherit करते हैं, इसलिए उन्हें अपना नहीं चाहिए। वे speak() को अपने specific behaviour के साथ override करते हैं।
super()
super() parent class से एक method को कॉल करता है। इसे use करें जब आप parent के behaviour को extend करना चाहते हैं rather than पूरी तरह से replace करते हैं: parent के __init__ को कॉल करें इसके setup को चलाने के लिए, फिर कुछ भी अपने subclass को need है top पर add करें।
class Animal:
def __init__(self, name, sound):
self.name = name
self.sound = sound
class Dog(Animal):
def __init__(self, name):
super().__init__(name, "Woof") # call Animal.__init__
self.tricks = [] # add something extra
def learn(self, trick):
self.tricks.append(trick)
rex = Dog("Rex")
rex.learn("sit")
print(rex.tricks) # ["sit"]हमेशा super().__init__() को कॉल करें जब आपके subclass के पास अपना __init__ हो और parent के पास भी हो।
super() parent class को reach करता है, इसलिए super().__init__() parent का setup चलाता है अपनी चीज़ें add करने से पहले। इसे use करें जब आपका subclass अपना `__init__` लिखता है और parent के पास एक भी हो। इसे skip करें और parent का setup कभी नहीं चलता, ऑब्जेक्ट को half-built छोड़ते हुए। Class methods और static methods
@classmethod एक method बनाता है जो instance की जगह class को receive करता है। यह alternative constructors के लिए उपयोगी है: एक string, एक file, या दूसरे format से एक instance बनाना। @staticmethod एक plain function है जो organisational reasons के लिए class के अंदर रहती है; यह instance न तो class को receive करता है।
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("Alice,87")class Player:
@staticmethod
def is_valid_name(name):
return name.isalpha() and len(name) >= 2
Player.is_valid_name("Alice") # True
Player.is_valid_name("A1") # FalseAlternative constructors के लिए @classmethod का use करें। Utility functions के लिए @staticmethod का use करें जो logically class के साथ belong करते हैं लेकिन instance या class data की need नहीं है।
@classmethod आपको class की जगह instance देता है, जो इसे alternative constructors के लिए go-to बनाता है: एक `Player` को string, file, जो format भी आपके पास हो से बनाएं। @staticmethod एक ordinary function है class के अंदर tidiness के लिए tucked; यह class न तो instance को न class को लेता है। Plain methods एक ऑब्जेक्ट के डेटा को touch करते हैं, ये दोनों नहीं। @property
@property आपको एक method को attribute की तरह access करने देता है, कोई parentheses की need नहीं। इसे values के लिए use करें जो अन्य attributes से computed हैं और simple attribute access के रूप में पढ़ने के लिए natural feel करते हैं।
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 (looks like an attribute, runs like a method)
print(c.diameter) # 10Properties computed values के लिए उपयोगी हैं: चीज़ें जो अन्य attributes से derived हैं जो () के बिना access करने के लिए natural लगती हैं।
@property आपको एक method को एक attribute की तरह पढ़ने देता है, कोई parentheses: c.area की जगह c.area()। यह values में fit करता है जो अन्य attributes से काम किए गए हैं और plain data के रूप में पढ़ने के लिए natural लगते हैं। Behind the scenes यह अभी भी हर बार जब आप इसे access करते हैं तब आपकी method चलाता है। Practice में
एक Player class instance attributes, methods, एक @property, और __str__ के साथ:
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")
alice.earn_points(50)
alice.take_hit()
print(alice) # "Alice | Score: 50 | Lives: 2"
print(alice.is_alive) # True
