डेटा क्लासेस Python की उन विशेषताओं में से हैं जिन्हें एक बार समझ लेने के बाद आप पुराने तरीके पर वापस नहीं जाते। इस सामान्य क्लास पर विचार करें:
classExercise:def__init__(self, name, reps, sets, weight):
self.name = name
self.reps = reps
self.sets = sets
self.weight = weight
संचालित द्वारा
मेरे लिए, यह क्लास डेफिनिशन काफी अल्प-कार्यकुशल है — __init__ मेथड में, आप हर पैरामीटर को कम-से-कम तीन बार दोहराते हैं। यह सुनने में बड़ी बात न लगे, लेकिन सोचिए, अपने करियर में आप कितनी बार अधिक पैरामीटर वाली क्लासेस लिखते हैं।
तुलना के लिए, ऊपर दिए गए कोड का डेटा क्लासेस विकल्प देखें:
from dataclasses import dataclass
@dataclassclassExercise:
name:str
reps:int
sets:int
weight:float# Weight in lbsसंचालित द्वारा
यह सादा-सा दिखने वाला कोड एक सामान्य क्लास से कई गुना बेहतर है। छोटा-सा @dataclass डेकोरेटर पर्दे के पीछे __init__, __repr__, __eq__ जैसी क्लासेस (मेथड) बना देता है, जिनके लिए हाथ से कम-से-कम 20 पंक्तियों का कोड लिखना पड़ता।
इसके अलावा, कई अन्य सुविधाएँ जैसे तुलना ऑपरेटर, ऑब्जेक्ट ऑर्डरिंग और इम्यूटेबिलिटी, हमारी क्लास के लिए एक ही पंक्ति में जादुई रूप से सक्षम की जा सकती हैं।
तो, इस ट्यूटोरियल का उद्देश्य आपको दिखाना है कि यदि आप ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग पसंद करते हैं, तो डेटा क्लासेस Python के लिए हुई सबसे अच्छी चीजों में से क्यों हैं।
चलिए शुरू करते हैं!
Python डेटा क्लासेस की बुनियाद
आइए Python डेटा क्लासेस की कुछ मूलभूत अवधारणाएँ कवर करें जो इन्हें इतना उपयोगी बनाती हैं।
डेटा क्लासेस में कुछ मेथड अपने-आप जनरेट हो जाते हैं
अपनी सभी खूबियों के बावजूद, डेटा क्लासेस सामान्य क्लासेस ही हैं जिनसे वही कार्यक्षमता बहुत कम कोड में मिल जाती है। यहाँ Exercise क्लास फिर से देखें:
from dataclasses import dataclass
@dataclassclassExercise:
name:str
reps:int
sets:int
weight:float
ex1 = Exercise("Bench press",10,3,52.5)# Verifying Exercise is a regular class
ex1.name
'Bench press'संचालित द्वारा
इस समय, Exercise में पहले से ही __repr__ और __eq__ मेथड इम्प्लीमेंटेड हैं। आइए जाँचते हैं:
repr(ex1)"Exercise(name='Bench press', reps=10, sets=3, weight=52.5)"संचालित द्वारा
किसी ऑब्जेक्ट का प्रतिनिधित्व repr ऐसा कोड लौटाना चाहिए जो स्वयं को पुनः बना सके, और हम देख सकते हैं कि ex1 के लिए बिल्कुल यही हो रहा है।
इसके विपरीत, पुराने तरीके से परिभाषित Exercise ऐसा दिखेगा:
classExercise:def__init__(self, name, reps, sets, weight):
self.name = name
self.reps = reps
self.sets = sets
self.weight = weight
ex3 = Exercise("Bench press",10,3,52.5)
ex3
<__main__.Exercise at 0x7f6834100130>संचालित द्वारा
काफी बेडौल और बेकार-सा लगता है!
अब, आइए __eq__ यानी इक्वैलिटी ऑपरेटर की मौजूदगी जाँचें:
# Redefine the class@dataclassclassExercise:
name:str
reps:int
sets:int
weight:float
ex1 = Exercise("Bench press",10,3,52.5)
ex2 = Exercise("Bench press",10,3,52.5)संचालित द्वारा
क्लास को अपने ही समान और समान पैरामीटर वाली दूसरी क्लास से तुलना करने पर True आना चाहिए:
और वैसा ही होता है! सामान्य क्लासेस में यह लॉजिक लिखना झंझटभरा होता।
डेटा क्लासेस में टाइप हिंट जरूरी हैं
जैसा कि आपने देखा होगा, फील्ड्स परिभाषित करते समय डेटा क्लासेस में टाइप हिंट जरूरी होते हैं। दरअसल, डेटा क्लासेस typing मॉड्यूल के किसी भी टाइप की अनुमति देती हैं। उदाहरण के लिए, ऐसा फील्ड कैसे बनाएँ जो Any डेटा टाइप स्वीकार कर सके:
from typing import Any
@dataclassclassDummy:
attr: Anyसंचालित द्वारा
हालाँकि, Python की विशेषता यह है कि भले ही डेटा क्लासेस टाइप हिंट माँगती हैं, टाइप वास्तव में लागू नहीं कराए जाते।
उदाहरण के लिए, Exercise क्लास का ऐसा इंस्टेंस बनाना जिसमें पूरी तरह गलत डेटा टाइप हों, बिना एरर के चल जाएगा:
silly_exercise = Exercise("Bench press","ten","three sets",52.5)
silly_exercise.sets
“Three sets”संचालित द्वारा
यदि आप डेटा टाइप लागू कराना चाहते हैं, तो आपको Mypy जैसे टाइप चेकर का उपयोग करना होगा।
डेटा क्लासेस फील्ड्स में डिफॉल्ट वैल्यू स्वीकार करती हैं
अब तक, हमने अपनी क्लासेस में कोई डिफॉल्ट नहीं जोड़ा है। आइए इसे ठीक करें:
@dataclassclassExercise:
name:str="Push-ups"
reps:int=10
sets:int=3
weight:float=0# Now, all fields have defaults
ex5 = Exercise()
ex5
Exercise(name='Push-ups', reps=10, sets=3, weight=0)संचालित द्वारा
ध्यान रखें कि नॉन-डिफॉल्ट फील्ड्स, डिफॉल्ट फील्ड्स के बाद नहीं आ सकतीं। उदाहरण के लिए, नीचे का कोड एरर देगा:
@dataclassclassExercise:
name:str="Push-ups"
reps:int=10
sets:int=3
weight:float# NOT ALLOWED
ex5 = Exercise()
ex5
TypeError: non-default argument 'weight' follows default argumentसंचालित द्वारा
व्यवहार में, आप शायद ही कभी name: type = value सिंटैक्स से डिफॉल्ट्स तय करेंगे।
इसके बजाय, आप field फ़ंक्शन का उपयोग करेंगे, जो हर फील्ड की परिभाषा पर अधिक नियंत्रण देता है:
from dataclasses import field
@dataclassclassExercise:
name:str= field(default="Push-up")
reps:int= field(default=10)
sets:int= field(default=3)
weight:float= field(default=0)# Now, all fields have defaults
ex5 = Exercise()
ex5
Exercise(name='Push-up', reps=10, sets=3, weight=0)संचालित द्वारा
field फ़ंक्शन के और भी पैरामीटर हैं, जैसे:
repr
init
compare
default_factory
आदि। हम इन्हें आगे की धाराओं में चर्चा करेंगे।
डेटा क्लासेस एक फ़ंक्शन से भी बनाई जा सकती हैं
डेटा क्लासेस की बुनियाद पर एक अंतिम टिप्पणी यह है कि make_dataclass फ़ंक्शन का उपयोग करके इनकी परिभाषा और भी छोटी हो सकती है:
from dataclasses import make_dataclass
Exercise = make_dataclass("Exercise",[("name",str),("reps",int),("sets",int),("weight",float),],)
ex3 = Exercise("Deadlifts",8,3,69.0)
ex3
Exercise(name='Deadlifts', reps=8, sets=3, weight=69.0)संचालित द्वारा
लेकिन आप पठनीयता का त्याग करेंगे, इसलिए मैं इस फ़ंक्शन का उपयोग करने की सलाह नहीं देता।
एडवांस्ड Python डेटा क्लासेस
इस खंड में, हम डेटा क्लासेस की उन्नत सुविधाओं पर चर्चा करेंगे जो और लाभ देती हैं। ऐसी ही एक सुविधा है डिफॉल्ट फ़ैक्टरी।
डिफॉल्ट फ़ैक्टरीज़
डिफॉल्ट फ़ैक्टरी समझाने के लिए, WorkoutSession नाम की एक और क्लास बनाते हैं जो दो फील्ड स्वीकार करती है:
from dataclasses import dataclass
from typing import List
@dataclassclassExercise:
name:str="Push-ups"
reps:int=10
sets:int=3
weight:float=0@dataclassclassWorkoutSession:
exercises: List[Exercise]
duration_minutes:intसंचालित द्वारा
List टाइप का उपयोग करके, हम यह निर्दिष्ट कर रहे हैं कि WorkoutSessionExercise इंस्टेंसेज़ की एक सूची स्वीकार करता है।
# Define the Exercise instances for HIIT training
ex1 = Exercise(name="Burpees", reps=15, sets=3)
ex2 = Exercise(name="Mountain Climbers", reps=20, sets=3)
ex3 = Exercise(name="Jump Squats", reps=12, sets=3)
exercises_monday =[ex1, ex2, ex3]
hiit_monday = WorkoutSession(exercises=exercises_monday, duration_minutes=30)संचालित द्वारा
अभी, हर वर्कआउट सेशन इंस्टेंस को इनिशियलाइज़ करते समय एक्सरसाइज़ देनी पड़ती हैं। लेकिन लोग ऐसे वर्कआउट नहीं करते — पहले वे सेशन शुरू करते हैं (शायद किसी ऐप में), और फिर करते-करते एक्सरसाइज़ जोड़ते हैं।
इसलिए, हमें बिना एक्सरसाइज़ और बिना ड्यूरेशन के सेशन बना सकने चाहिए। आइए exercises के लिए खाली सूची को डिफॉल्ट वैल्यू के रूप में जोड़कर यह करें:
@dataclassclassWorkoutSession:
exercises: List[Exercise]=[]
duration_minutes:int=None
hiit_monday = WorkoutSession("25-02-2024")
ValueError: mutable default <class'list'>for field exercises isnot allowed: use default_factoryसंचालित द्वारा
लेकिन हमें एरर मिला — दरअसल, डेटा क्लासेस म्यूटेबल डिफॉल्ट वैल्यू की अनुमति नहीं देतीं।
सौभाग्य से, हम इसे डिफॉल्ट फ़ैक्टरी का उपयोग करके ठीक कर सकते हैं:
default_factory पैरामीटर किसी फ़ंक्शन को स्वीकार करता है जो डेटा क्लास फील्ड के लिए आरंभिक मान लौटाता है। यानी यह कोई भी मनचाहा फ़ंक्शन स्वीकार कर सकता है:
tuple
dict
set
कोई भी यूज़र-डिफाइंड कस्टम फ़ंक्शन
यह बात इस पर निर्भर नहीं करती कि फ़ंक्शन का परिणाम म्यूटेबल है या नहीं।
अब, सोचें तो अधिकतर लोग अपने प्रशिक्षण की शुरुआत वार्म-अप एक्सरसाइज़ से करते हैं, जो लगभग हर तरह के वर्कआउट के लिए मिलती-जुलती होती हैं। इसलिए, बिना एक्सरसाइज़ के सेशन इनिशियलाइज़ करना कुछ लोगों को नहीं भा सकता।
इसके बजाय, एक फ़ंक्शन बनाते हैं जो तीन वार्म-अप Exercises लौटाए:
defcreate_warmup():return[
Exercise("Jumping jacks",30,1),
Exercise("Squat lunges",10,2),
Exercise("High jumps",20,1),]@dataclassclassWorkoutSession:
exercises: List[Exercise]= field(default_factory=create_warmup)
duration_minutes:int=5# Increase the default duration as well
hiit_monday = WorkoutSession()
hiit_monday
WorkoutSession(exercises=[Exercise(name='Jumping jacks', reps=30, sets=1, weight=0), Exercise(name='Squat lunges', reps=10, sets=2, weight=0), Exercise(name='High jumps', reps=20, sets=1, weight=0)], duration_minutes=5)संचालित द्वारा
अब, जब भी हम कोई सेशन बनाएँगे, वे कुछ वार्म-अप एक्सरसाइज़ के साथ पहले से लॉग होकर आएँगे। WorkoutSession का नया संस्करण इस बात के लिए डिफॉल्ट ड्यूरेशन पाँच मिनट रखता है।
डेटा क्लासेस में मेथड जोड़ना
क्योंकि डेटा क्लासेस सामान्य क्लासेस ही हैं, इनमें मेथड जोड़ना भी वही रहता है। अपनी WorkoutSession डेटा क्लास में दो मेथड जोड़ते हैं:
इन मेथड्स से अब हम किसी भी नई गतिविधि को सेशन में लॉग कर सकते हैं:
hiit_monday = WorkoutSession()# Log a new exercise
new_exercise = Exercise("Deadlifts",6,4,60)
hiit_monday.add_exercise(new_exercise)
hiit_monday.increase_duration(15)संचालित द्वारा
क्लास की स्ट्रिंग रेप्रेज़ेंटेशन कहीं बेहतर है। अब, WorkoutSession भी ठीक करते हैं:
@dataclassclassWorkoutSession:
exercises: List[Exercise]= field(default_factory=create_warmup)
duration_minutes:int=5# Increase the default duration as welldefadd_exercise(self, exercise: Exercise):
self.exercises.append(exercise)defincrease_duration(self, minutes:int):
self.duration_minutes += minutes
def__str__(self):
base =""for ex in self.exercises:
base +=str(ex)+"\n"
base +=f"\nSession duration: {self.duration_minutes} minutes."return base
hiit_monday = WorkoutSession()print(hiit_monday)
Jumping jacks:30/1
Squat lunges:10/2
High jumps:20/1
Session duration:5 minutes.संचालित द्वारा
ध्यान दें: कोड की पंक्ति-दर-पंक्ति व्याख्या के लिए स्निपेट के नीचे दिए “Explain code” बटन का उपयोग करें।
अब हमारे पास पठनीय और सुस्पष्ट आउटपुट है।
डेटा क्लासेस में तुलना
कई क्लासेस के लिए, उनके ऑब्जेक्ट्स की कुछ लॉजिक से तुलना करना समझदारी होती है। वर्कआउट के लिए, यह ड्यूरेशन, एक्सरसाइज़ की तीव्रता या वेट हो सकता है।
पहले देखते हैं कि मौजूदा स्थिति में दो वर्कआउट की तुलना करने पर क्या होता है:
hiit_wednesday = WorkoutSession()
hiit_wednesday.add_exercise(Exercise("Pull-ups",7,3))print(hiit_wednesday)
Jumping jacks:30/1
Squat lunges:10/2
High jumps:20/1
Pull-ups:7/3
Session duration:5 minutes.
hiit_monday > hiit_wednesday
TypeError:'>'not supported between instances of 'WorkoutSession'and'WorkoutSession'संचालित द्वारा
हमें TypeError मिलता है क्योंकि डेटा क्लासेस तुलना ऑपरेटर इम्प्लीमेंट नहीं करतीं। लेकिन इसे order पैरामीटर True कर के आसानी से ठीक किया जा सकता है:
इस बार तुलना काम करती है, लेकिन हम तुलना किस आधार पर कर रहे हैं?
डेटा क्लासेस में तुलना फील्ड्स के परिभाषित क्रम में होती है। अभी, तुलना वर्कआउट ड्यूरेशन के आधार पर हो रही है क्योंकि पहला फील्ड exercises नॉन-स्टैंडर्ड ऑब्जेक्ट्स रखता है।
हम इसे बुधवार वाले सेशन की ड्यूरेशन बढ़ाकर जाँच सकते हैं:
किसी भी फील्ड के लिए compare को False करने से हम उसे सॉर्टिंग से बाहर कर देते हैं, जैसा कि ऊपर के परिणाम से साबित है।
पोस्ट-इनिट फील्ड मैनिप्युलेशन
अभी, वार्म-अप एक्सरसाइज़ को ध्यान में रखते हुए हमारे पास पाँच मिनट का डिफॉल्ट सेशन ड्यूरेशन है। लेकिन यह तभी तर्कसंगत है जब यूज़र सेशन वार्म-अप से शुरू करे। अगर वे अन्य एक्सरसाइज़ से शुरू करें तो क्या होगा:
new_session = WorkoutSession([Exercise("Diamond push-ups",10,3)])
new_session.duration_minutes
5संचालित द्वारा
सिर्फ एक एक्सरसाइज़ के लिए कुल ड्यूरेशन पाँच मिनट होना तर्कसंगत नहीं। हर सेशन को हर एक्सरसाइज़ के सेटों की संख्या के आधार पर अपना ड्यूरेशन डायनेमिक रूप से अनुमान लगाना चाहिए। यानी duration_minutes को exercises फील्ड पर निर्भर होना चाहिए।
आइए इसे इम्प्लीमेंट करें:
@dataclassclassWorkoutSession:
exercises: List[Exercise]= field(default_factory=create_warmup)
duration_minutes:int= field(default=0, init=False)def__post_init__(self):
set_duration =3for ex in self.exercises:
self.duration_minutes += ex.sets * set_duration
...संचालित द्वारा
इस बार, हम duration_minutes को init को False रखकर परिभाषित कर रहे हैं ताकि फील्ड का इनिशियलाइज़ेशन टाला जा सके।
फिर, विशेष मेथड __post_init__ के अंदर, हम इसके मान को हर Exercise के कुल सेटों के आधार पर अपडेट कर रहे हैं।
अब, जब हम WorkoutSession इनिशियलाइज़ करते हैं, तो duration_minutes हर एक्सरसाइज़ के हर सेट के लिए तीन मिनट से डायनेमिक रूप से बढ़ता है।
# Adding an exercise with three sets
hiit_friday = WorkoutSession([Exercise("Diamond push-ups",10,3)])
hiit_friday.duration_minutes
9संचालित द्वारा
सामान्यतः, यदि आप ऐसा फील्ड परिभाषित करना चाहते हैं जो आपकी डेटा क्लास के अन्य फील्ड्स पर निर्भर हो, तो आप __post_init__ लॉजिक का उपयोग कर सकते हैं।
डेटा क्लासेस में इम्यूटेबिलिटी
हमारी WorkoutSession डेटा क्लास लगभग तैयार है; बस इसे सुरक्षित करना है। अभी, इसे आसानी से बिगाड़ा जा सकता है:
hiit_friday.duration_minutes =1000
hiit_friday.duration_minutes
1000del hiit_friday.exercisesसंचालित द्वारा
हम अपनी क्लासेस के सभी फील्ड्स को इस तरह सुरक्षित करना चाहते हैं कि उन्हें केवल हमारे इच्छित तरीके से ही बदला जा सके। इसे प्राप्त करने के लिए, @dataclass डेकोरेटर एक सुविधाजनक frozen आर्ग्युमेंट देता है:
@dataclass(frozen=True)classFrozenExercise:
name:str
reps:int
sets:int
weight:int|float=0
ex1 = FrozenExercise("Muscle-ups",5,3)संचालित द्वारा
अब, अगर हम कोई भी फील्ड बदलना चाहें, तो एरर मिलता है:
ex1.sets =5
FrozenInstanceError: cannot assign to field 'sets'संचालित द्वारा
frozen को True करने से हर फील्ड के लिए __deleteattr__ और __setattr__ मेथड अपने-आप जुड़ जाते हैं ताकि इनिशियलाइज़ेशन के बाद उन्हें डिलीट या अपडेट होने से बचाया जा सके। साथ ही, दूसरे लोग नए फील्ड भी नहीं जोड़ पाएँगे:
ex1.new_field =10
FrozenInstanceError: cannot assign to field 'new_field'संचालित द्वारा
पारंपरिक क्लासेस में यही कार्यक्षमता दर्जनों पंक्तियों में जाती।
हालाँकि, ध्यान दें कि हम अपनी क्लासेस को सच में इम्यूटेबल नहीं बना सकते। उदाहरण के लिए, WorkoutSession को frozen को True कर के फिर से लिखते हैं:
@dataclass(frozen=True)classImmutableWorkoutSession:
exercises: List[Exercise]= field(default_factory=create_warmup)
duration_minutes:int=5
session1 = ImmutableWorkoutSession()संचालित द्वारा
अपेक्षित रूप से, हम एक्सरसाइज़ की सूची को सीधे संशोधित नहीं कर सकते:
लेकिन, exercises एक सूची है, जो पूरी तरह म्यूटेबल है, जिससे नीचे का ऑपरेशन संभव हो जाता है:
# सूची के एक एलिमेंट को बदलना
# Changing one of the elements in a list
session1.exercises[1]= FrozenExercise("Totally new exercise",5,5)print(session1)
ImmutableWorkoutSession(exercises=[Exercise(name='Jumping jacks', reps=30, sets=1, weight=0), FrozenExercise(name='Totally new exercise', reps=5, sets=5, weight=0), Exercise(name='High jumps', reps=20, sets=1, weight=0)], duration_minutes=5)संचालित द्वारा
इसलिए, आकस्मिक बदलावों से बचाने के लिए, फील्ड वैल्यू के लिए tuples जैसे इम्यूटेबल ऑब्जेक्ट्स का उपयोग करने की सिफारिश की जाती है।
डेटा क्लासेस में इनहेरिटेंस
अंत में, हम पैरेंट और चाइल्ड क्लासेस में फील्ड्स के क्रम को कवर करेंगे।
क्योंकि डेटा क्लासेस सामान्य क्लासेस हैं, इनहेरिटेंस सामान्य रूप से काम करता है:
@dataclass(frozen=True)classImmutableWorkoutSession:
exercises: List[Exercise]= field(default_factory=create_warmup)
duration_minutes:int=5@dataclass(frozen=True)classCardioWorkoutSession(ImmutableWorkoutSession):passसंचालित द्वारा
लेकिन, क्योंकि पैरेंट क्लास (ImmutableWorkoutSession) का अंतिम फील्ड डिफॉल्ट वैल्यू रखता है, चाइल्ड क्लासेस के सभी फील्ड्स में भी डिफॉल्ट वैल्यू होनी चाहिए।
उदाहरण के लिए, यह अनुमति नहीं है:
@dataclass(frozen=True)classImmutableWorkoutSession:
exercises: List[Exercise]= field(default_factory=create_warmup)
duration_minutes:int=5@dataclass(frozen=True)classCardioWorkoutSession(ImmutableWorkoutSession):
intensity_level:str# Not allowed, must have a default
TypeError: non-default argument 'intensity_level' follows default argument
संचालित द्वारा
डेटा क्लासेस की कमियाँ और आगे के संसाधन
Python 3.7 से डेटा क्लासेस लगातार बेहतर होती गई हैं (शुरू से ही बेहतरीन थीं) और उन कई उपयोग मामलों को कवर करती हैं जहाँ आपको क्लासेस लिखने की ज़रूरत पड़ती है। लेकिन ये निम्न परिदृश्यों में कम अनुकूल हो सकती हैं:
कस्टम __init__ मेथड्स
कस्टम __new__ मेथड्स
विभिन्न इनहेरिटेंस पैटर्न
और भी बहुत से, जिन पर इस बेहतरीन Reddit थ्रेड में चर्चा है। यदि आप यह विस्तार से समझना चाहते हैं कि डेटा क्लासेस क्यों पेश की गईं और वे सामान्य क्लास डेफिनिशन का ड्रॉप-इन रिप्लेसमेंट क्यों नहीं हैं, तो PEP 557 पढ़ें।
यदि आप सामान्य रूप से ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग में रुचि रखते हैं, तो अपनी यात्रा आगे बढ़ाने के लिए यह कोर्स देखें:
मूलतः, डेटा क्लासेस डेटा को अधिक कुशलता से रखने और प्राप्त करने की थोड़ी ज्यादा परिष्कृत संरचनाएँ हैं। हालाँकि, Python में कई अन्य डेटा स्ट्रक्चर हैं जो यह काम कमोबेश इसी तरह करते हैं। उदाहरण के लिए, आप Data Types for Data Science कोर्स के आखिरी अध्याय में counters, defaultdicts और namedtuples के बारे में सीख सकते हैं।
मैं 2 से अधिक वर्षों के अनुभव वाला डेटा साइंस कंटेंट क्रिएटर हूँ और Medium पर सबसे बड़े फॉलोइंग्स में से एक रखता हूँ। मुझे एआई और एमएल पर विस्तार से लेख लिखना पसंद है, जिसमें हल्का-सा व्यंग्यात्मक अंदाज रहता है—क्योंकि कुछ तो करना पड़ता है ताकि वे कम उबाऊ लगें। मैंने 130 से अधिक लेख और एक DataCamp कोर्स तैयार किया है, और एक और बन रहा है। मेरा कंटेंट 50 लाख से अधिक लोगों ने देखा है, जिनमें से 20 हज़ार लोग Medium और LinkedIn पर फॉलोअर्स बन गए।
डेटा क्लासेस Python की उन विशेषताओं में से हैं जिन्हें एक बार समझ लेने के बाद आप पुराने तरीके पर वापस नहीं जाते। इस सामान्य क्लास पर विचार करें:
मेरे लिए, यह क्लास डेफिनिशन काफी अल्प-कार्यकुशल है —
__init__मेथड में, आप हर पैरामीटर को कम-से-कम तीन बार दोहराते हैं। यह सुनने में बड़ी बात न लगे, लेकिन सोचिए, अपने करियर में आप कितनी बार अधिक पैरामीटर वाली क्लासेस लिखते हैं।तुलना के लिए, ऊपर दिए गए कोड का डेटा क्लासेस विकल्प देखें:
यह सादा-सा दिखने वाला कोड एक सामान्य क्लास से कई गुना बेहतर है। छोटा-सा
@dataclassडेकोरेटर पर्दे के पीछे__init__,__repr__,__eq__जैसी क्लासेस (मेथड) बना देता है, जिनके लिए हाथ से कम-से-कम 20 पंक्तियों का कोड लिखना पड़ता।इसके अलावा, कई अन्य सुविधाएँ जैसे तुलना ऑपरेटर, ऑब्जेक्ट ऑर्डरिंग और इम्यूटेबिलिटी, हमारी क्लास के लिए एक ही पंक्ति में जादुई रूप से सक्षम की जा सकती हैं।
तो, इस ट्यूटोरियल का उद्देश्य आपको दिखाना है कि यदि आप ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग पसंद करते हैं, तो डेटा क्लासेस Python के लिए हुई सबसे अच्छी चीजों में से क्यों हैं।
चलिए शुरू करते हैं!
Python डेटा क्लासेस की बुनियाद
आइए Python डेटा क्लासेस की कुछ मूलभूत अवधारणाएँ कवर करें जो इन्हें इतना उपयोगी बनाती हैं।
डेटा क्लासेस में कुछ मेथड अपने-आप जनरेट हो जाते हैं
अपनी सभी खूबियों के बावजूद, डेटा क्लासेस सामान्य क्लासेस ही हैं जिनसे वही कार्यक्षमता बहुत कम कोड में मिल जाती है। यहाँ
Exerciseक्लास फिर से देखें:इस समय,
Exerciseमें पहले से ही__repr__और__eq__मेथड इम्प्लीमेंटेड हैं। आइए जाँचते हैं:किसी ऑब्जेक्ट का प्रतिनिधित्व
reprऐसा कोड लौटाना चाहिए जो स्वयं को पुनः बना सके, और हम देख सकते हैं किex1के लिए बिल्कुल यही हो रहा है।इसके विपरीत, पुराने तरीके से परिभाषित
Exerciseऐसा दिखेगा:काफी बेडौल और बेकार-सा लगता है!
अब, आइए
__eq__यानी इक्वैलिटी ऑपरेटर की मौजूदगी जाँचें:क्लास को अपने ही समान और समान पैरामीटर वाली दूसरी क्लास से तुलना करने पर True आना चाहिए:
और वैसा ही होता है! सामान्य क्लासेस में यह लॉजिक लिखना झंझटभरा होता।
डेटा क्लासेस में टाइप हिंट जरूरी हैं
जैसा कि आपने देखा होगा, फील्ड्स परिभाषित करते समय डेटा क्लासेस में टाइप हिंट जरूरी होते हैं। दरअसल, डेटा क्लासेस
typingमॉड्यूल के किसी भी टाइप की अनुमति देती हैं। उदाहरण के लिए, ऐसा फील्ड कैसे बनाएँ जोAnyडेटा टाइप स्वीकार कर सके:हालाँकि, Python की विशेषता यह है कि भले ही डेटा क्लासेस टाइप हिंट माँगती हैं, टाइप वास्तव में लागू नहीं कराए जाते।
उदाहरण के लिए,
Exerciseक्लास का ऐसा इंस्टेंस बनाना जिसमें पूरी तरह गलत डेटा टाइप हों, बिना एरर के चल जाएगा:यदि आप डेटा टाइप लागू कराना चाहते हैं, तो आपको Mypy जैसे टाइप चेकर का उपयोग करना होगा।
डेटा क्लासेस फील्ड्स में डिफॉल्ट वैल्यू स्वीकार करती हैं
अब तक, हमने अपनी क्लासेस में कोई डिफॉल्ट नहीं जोड़ा है। आइए इसे ठीक करें:
ध्यान रखें कि नॉन-डिफॉल्ट फील्ड्स, डिफॉल्ट फील्ड्स के बाद नहीं आ सकतीं। उदाहरण के लिए, नीचे का कोड एरर देगा:
व्यवहार में, आप शायद ही कभी
name: type = valueसिंटैक्स से डिफॉल्ट्स तय करेंगे।इसके बजाय, आप
fieldफ़ंक्शन का उपयोग करेंगे, जो हर फील्ड की परिभाषा पर अधिक नियंत्रण देता है:fieldफ़ंक्शन के और भी पैरामीटर हैं, जैसे:reprinitcomparedefault_factoryआदि। हम इन्हें आगे की धाराओं में चर्चा करेंगे।
डेटा क्लासेस एक फ़ंक्शन से भी बनाई जा सकती हैं
डेटा क्लासेस की बुनियाद पर एक अंतिम टिप्पणी यह है कि
make_dataclassफ़ंक्शन का उपयोग करके इनकी परिभाषा और भी छोटी हो सकती है:लेकिन आप पठनीयता का त्याग करेंगे, इसलिए मैं इस फ़ंक्शन का उपयोग करने की सलाह नहीं देता।
एडवांस्ड Python डेटा क्लासेस
इस खंड में, हम डेटा क्लासेस की उन्नत सुविधाओं पर चर्चा करेंगे जो और लाभ देती हैं। ऐसी ही एक सुविधा है डिफॉल्ट फ़ैक्टरी।
डिफॉल्ट फ़ैक्टरीज़
डिफॉल्ट फ़ैक्टरी समझाने के लिए,
WorkoutSessionनाम की एक और क्लास बनाते हैं जो दो फील्ड स्वीकार करती है:Listटाइप का उपयोग करके, हम यह निर्दिष्ट कर रहे हैं किWorkoutSessionExerciseइंस्टेंसेज़ की एक सूची स्वीकार करता है।अभी, हर वर्कआउट सेशन इंस्टेंस को इनिशियलाइज़ करते समय एक्सरसाइज़ देनी पड़ती हैं। लेकिन लोग ऐसे वर्कआउट नहीं करते — पहले वे सेशन शुरू करते हैं (शायद किसी ऐप में), और फिर करते-करते एक्सरसाइज़ जोड़ते हैं।
इसलिए, हमें बिना एक्सरसाइज़ और बिना ड्यूरेशन के सेशन बना सकने चाहिए। आइए
exercisesके लिए खाली सूची को डिफॉल्ट वैल्यू के रूप में जोड़कर यह करें:लेकिन हमें एरर मिला — दरअसल, डेटा क्लासेस म्यूटेबल डिफॉल्ट वैल्यू की अनुमति नहीं देतीं।
सौभाग्य से, हम इसे डिफॉल्ट फ़ैक्टरी का उपयोग करके ठीक कर सकते हैं:
default_factoryपैरामीटर किसी फ़ंक्शन को स्वीकार करता है जो डेटा क्लास फील्ड के लिए आरंभिक मान लौटाता है। यानी यह कोई भी मनचाहा फ़ंक्शन स्वीकार कर सकता है:tupledictsetयह बात इस पर निर्भर नहीं करती कि फ़ंक्शन का परिणाम म्यूटेबल है या नहीं।
अब, सोचें तो अधिकतर लोग अपने प्रशिक्षण की शुरुआत वार्म-अप एक्सरसाइज़ से करते हैं, जो लगभग हर तरह के वर्कआउट के लिए मिलती-जुलती होती हैं। इसलिए, बिना एक्सरसाइज़ के सेशन इनिशियलाइज़ करना कुछ लोगों को नहीं भा सकता।
इसके बजाय, एक फ़ंक्शन बनाते हैं जो तीन वार्म-अप
Exercisesलौटाए:अब, जब भी हम कोई सेशन बनाएँगे, वे कुछ वार्म-अप एक्सरसाइज़ के साथ पहले से लॉग होकर आएँगे।
WorkoutSessionका नया संस्करण इस बात के लिए डिफॉल्ट ड्यूरेशन पाँच मिनट रखता है।डेटा क्लासेस में मेथड जोड़ना
क्योंकि डेटा क्लासेस सामान्य क्लासेस ही हैं, इनमें मेथड जोड़ना भी वही रहता है। अपनी
WorkoutSessionडेटा क्लास में दो मेथड जोड़ते हैं:इन मेथड्स से अब हम किसी भी नई गतिविधि को सेशन में लॉग कर सकते हैं:
लेकिन एक समस्या है:
सेशन को प्रिंट करने पर इसकी डिफॉल्ट रेप्रेज़ेंटेशन बहुत लंबी और अपठनीय है, क्योंकि इसमें ऑब्जेक्ट को पुनः बनाने वाला कोड आता है। आइए इसे ठीक करें।
__repr__और__str__डेटा क्लासेस मेंडेटा क्लासेस
__repr__ऑटोमेटिकली इम्प्लीमेंट करती हैं, लेकिन__str__नहीं। इसलिएprintकॉल करने पर क्लास__repr__पर लौट आती है।तो,
__str__को स्वयं परिभाषित करके इस व्यवहार को ओवरराइड करते हैं:__repr__वही बना रहता है, लेकिनprintकॉल करने पर:क्लास की स्ट्रिंग रेप्रेज़ेंटेशन कहीं बेहतर है। अब,
WorkoutSessionभी ठीक करते हैं:ध्यान दें: कोड की पंक्ति-दर-पंक्ति व्याख्या के लिए स्निपेट के नीचे दिए “Explain code” बटन का उपयोग करें।
अब हमारे पास पठनीय और सुस्पष्ट आउटपुट है।
डेटा क्लासेस में तुलना
कई क्लासेस के लिए, उनके ऑब्जेक्ट्स की कुछ लॉजिक से तुलना करना समझदारी होती है। वर्कआउट के लिए, यह ड्यूरेशन, एक्सरसाइज़ की तीव्रता या वेट हो सकता है।
पहले देखते हैं कि मौजूदा स्थिति में दो वर्कआउट की तुलना करने पर क्या होता है:
हमें
TypeErrorमिलता है क्योंकि डेटा क्लासेस तुलना ऑपरेटर इम्प्लीमेंट नहीं करतीं। लेकिन इसेorderपैरामीटरTrueकर के आसानी से ठीक किया जा सकता है:इस बार तुलना काम करती है, लेकिन हम तुलना किस आधार पर कर रहे हैं?
डेटा क्लासेस में तुलना फील्ड्स के परिभाषित क्रम में होती है। अभी, तुलना वर्कआउट ड्यूरेशन के आधार पर हो रही है क्योंकि पहला फील्ड
exercisesनॉन-स्टैंडर्ड ऑब्जेक्ट्स रखता है।हम इसे बुधवार वाले सेशन की ड्यूरेशन बढ़ाकर जाँच सकते हैं:
अपेक्षित रूप से, हमें
Falseमिला।लेकिन क्या होगा अगर
Workoutका पहला फील्ड किसी और प्रकार का हो, जैसे कि स्ट्रिंग? आज़माते हैं:भले ही सोमवार का सेशन लम्बा हो, तुलना बताती है कि वह बुधवार से छोटा है। कारण यह है कि Python स्ट्रिंग तुलना में “25” “27” से पहले आता है।
तो, फील्ड्स का क्रम बनाए रखते हुए सेशंस को वर्कआउट ड्यूरेशन के आधार पर कैसे सॉर्ट करें? यह
fieldफ़ंक्शन से सरल है:किसी भी फील्ड के लिए
compareकोFalseकरने से हम उसे सॉर्टिंग से बाहर कर देते हैं, जैसा कि ऊपर के परिणाम से साबित है।पोस्ट-इनिट फील्ड मैनिप्युलेशन
अभी, वार्म-अप एक्सरसाइज़ को ध्यान में रखते हुए हमारे पास पाँच मिनट का डिफॉल्ट सेशन ड्यूरेशन है। लेकिन यह तभी तर्कसंगत है जब यूज़र सेशन वार्म-अप से शुरू करे। अगर वे अन्य एक्सरसाइज़ से शुरू करें तो क्या होगा:
सिर्फ एक एक्सरसाइज़ के लिए कुल ड्यूरेशन पाँच मिनट होना तर्कसंगत नहीं। हर सेशन को हर एक्सरसाइज़ के सेटों की संख्या के आधार पर अपना ड्यूरेशन डायनेमिक रूप से अनुमान लगाना चाहिए। यानी
duration_minutesकोexercisesफील्ड पर निर्भर होना चाहिए।आइए इसे इम्प्लीमेंट करें:
इस बार, हम
duration_minutesकोinitकोFalseरखकर परिभाषित कर रहे हैं ताकि फील्ड का इनिशियलाइज़ेशन टाला जा सके।फिर, विशेष मेथड
__post_init__के अंदर, हम इसके मान को हरExerciseके कुल सेटों के आधार पर अपडेट कर रहे हैं।अब, जब हम
WorkoutSessionइनिशियलाइज़ करते हैं, तोduration_minutesहर एक्सरसाइज़ के हर सेट के लिए तीन मिनट से डायनेमिक रूप से बढ़ता है।सामान्यतः, यदि आप ऐसा फील्ड परिभाषित करना चाहते हैं जो आपकी डेटा क्लास के अन्य फील्ड्स पर निर्भर हो, तो आप
__post_init__लॉजिक का उपयोग कर सकते हैं।डेटा क्लासेस में इम्यूटेबिलिटी
हमारी
WorkoutSessionडेटा क्लास लगभग तैयार है; बस इसे सुरक्षित करना है। अभी, इसे आसानी से बिगाड़ा जा सकता है:हम अपनी क्लासेस के सभी फील्ड्स को इस तरह सुरक्षित करना चाहते हैं कि उन्हें केवल हमारे इच्छित तरीके से ही बदला जा सके। इसे प्राप्त करने के लिए,
@dataclassडेकोरेटर एक सुविधाजनकfrozenआर्ग्युमेंट देता है:अब, अगर हम कोई भी फील्ड बदलना चाहें, तो एरर मिलता है:
frozenकोTrueकरने से हर फील्ड के लिए__deleteattr__और__setattr__मेथड अपने-आप जुड़ जाते हैं ताकि इनिशियलाइज़ेशन के बाद उन्हें डिलीट या अपडेट होने से बचाया जा सके। साथ ही, दूसरे लोग नए फील्ड भी नहीं जोड़ पाएँगे:पारंपरिक क्लासेस में यही कार्यक्षमता दर्जनों पंक्तियों में जाती।
हालाँकि, ध्यान दें कि हम अपनी क्लासेस को सच में इम्यूटेबल नहीं बना सकते। उदाहरण के लिए,
WorkoutSessionकोfrozenकोTrueकर के फिर से लिखते हैं:अपेक्षित रूप से, हम एक्सरसाइज़ की सूची को सीधे संशोधित नहीं कर सकते:
लेकिन,
exercisesएक सूची है, जो पूरी तरह म्यूटेबल है, जिससे नीचे का ऑपरेशन संभव हो जाता है:# सूची के एक एलिमेंट को बदलना
इसलिए, आकस्मिक बदलावों से बचाने के लिए, फील्ड वैल्यू के लिए tuples जैसे इम्यूटेबल ऑब्जेक्ट्स का उपयोग करने की सिफारिश की जाती है।
डेटा क्लासेस में इनहेरिटेंस
अंत में, हम पैरेंट और चाइल्ड क्लासेस में फील्ड्स के क्रम को कवर करेंगे।
क्योंकि डेटा क्लासेस सामान्य क्लासेस हैं, इनहेरिटेंस सामान्य रूप से काम करता है:
लेकिन, क्योंकि पैरेंट क्लास (
ImmutableWorkoutSession) का अंतिम फील्ड डिफॉल्ट वैल्यू रखता है, चाइल्ड क्लासेस के सभी फील्ड्स में भी डिफॉल्ट वैल्यू होनी चाहिए।उदाहरण के लिए, यह अनुमति नहीं है:
डेटा क्लासेस की कमियाँ और आगे के संसाधन
Python 3.7 से डेटा क्लासेस लगातार बेहतर होती गई हैं (शुरू से ही बेहतरीन थीं) और उन कई उपयोग मामलों को कवर करती हैं जहाँ आपको क्लासेस लिखने की ज़रूरत पड़ती है। लेकिन ये निम्न परिदृश्यों में कम अनुकूल हो सकती हैं:
__init__मेथड्स__new__मेथड्सऔर भी बहुत से, जिन पर इस बेहतरीन Reddit थ्रेड में चर्चा है। यदि आप यह विस्तार से समझना चाहते हैं कि डेटा क्लासेस क्यों पेश की गईं और वे सामान्य क्लास डेफिनिशन का ड्रॉप-इन रिप्लेसमेंट क्यों नहीं हैं, तो PEP 557 पढ़ें।
यदि आप सामान्य रूप से ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग में रुचि रखते हैं, तो अपनी यात्रा आगे बढ़ाने के लिए यह कोर्स देखें:
मूलतः, डेटा क्लासेस डेटा को अधिक कुशलता से रखने और प्राप्त करने की थोड़ी ज्यादा परिष्कृत संरचनाएँ हैं। हालाँकि, Python में कई अन्य डेटा स्ट्रक्चर हैं जो यह काम कमोबेश इसी तरह करते हैं। उदाहरण के लिए, आप Data Types for Data Science कोर्स के आखिरी अध्याय में counters,
defaultdictsऔरnamedtuplesके बारे में सीख सकते हैं।