Python OOP Concepts समजून घ्या – Real Examples

जेव्हा मी पहिल्यांदा Python शिकायला सुरुवात केली, तेव्हा functions आणि loops पर्यंत सगळं ठीक चाललं होतं. पण जेव्हा OOP (Object Oriented Programming) आलं, तेव्हा खरा गोंधळ सुरू झाला. “class म्हणजे काय?”, “object आणि class मध्ये फरक काय?”, “self का लिहायचं?” — हे प्रश्न मला आठवडाभर छळत होते.

आज लिहितोय ते फक्त textbook definitions नाहीत — तर मी स्वतः शिकताना जे कोड लिहिले, ज्या चुका केल्या, आणि ज्या error screenshots आले, त्या सगळ्यांसह हा blog आहे. त्यामुळे तुम्हाला हे फक्त “वाचायला” नाही, तर प्रत्यक्ष अनुभवातून शिकायला मिळेल. चला सुरुवात करूया.

Object Oriented Programming ही एक अशी पद्धत आहे जिथे आपण आपला कोड real-world वस्तूंसारखा (objects) organize करतो. उदाहरणार्थ — एक “कुत्रा” (Dog) ही वस्तू आहे. त्याचं नाव आहे, वय आहे (हे झालं data/attributes), आणि तो भुंकू शकतो, धावू शकतो (हे झालं behavior/methods).

Python मध्ये OOP चे चार मुख्य स्तंभ (Four Pillars) आहेत:

  1. Inheritance (वारसा)
  2. Polymorphism (बहुरूपता)
  3. Encapsulation (संकलन)
  4. Abstraction (अमूर्तीकरण)

पण त्याआधी आपल्याला Class आणि Object समजून घ्यावे लागतील, कारण संपूर्ण OOP याच दोन गोष्टींवर उभं आहे.

Class ही एक blueprint किंवा साचा आहे. जसं घर बांधण्याआधी architect एक नकाशा (design) बनवतो, तसाच class एक टेम्पलेट असतो, ज्यावरून पुढे अनेक objects तयार करता येतात.

मी जेव्हा पहिल्यांदा class लिहिला, तेव्हा मी “Student” चं उदाहरण घेतलं होतं, कारण मला ते जवळचं वाटलं:

class Student:
    school_name = "Zilla Parishad Shala"  # Class attribute

    def __init__(self, name, marks):
        self.name = name        # Instance attribute
        self.marks = marks      # Instance attribute

    def result(self):
        if self.marks >= 35:
            return f"{self.name} पास झाला/झाली आहे."
        else:
            return f"{self.name} नापास झाला/झाली आहे."

इथे काय होतंय ते समजून घेऊ:

  • class Student: — यामुळे Student नावाचा एक नवीन class तयार होतो.
  • school_name हे एक class attribute आहे — म्हणजे या class पासून बनणाऱ्या प्रत्येक object ला हे common असेल.
  • __init__() ही एक विशेष method आहे, जिला constructor म्हणतात. जेव्हा तुम्ही नवीन object तयार करता, तेव्हा ही method आपोआप चालते.
  • self म्हणजे “सध्याचा object स्वतः.” प्रत्येक object ला त्याचा स्वतःचा data ठेवण्यासाठी self लागतो.

माझी पहिली चूक — self विसरणे:

जेव्हा मी सुरुवातीला हा कोड लिहीत होतो, तेव्हा मी चुकून self parameter द्यायला विसरलो:

class Student:
    def __init__(name, marks):   # चुकीचं — self नाही!
        self.name = name
        self.marks = marks

आणि जेव्हा मी object तयार केला:

s1 = Student("Amit", 78)

तेव्हा मला हा error आला: TypeError: init() takes 2 positional arguments but 3 were given

हा error का येतो? ,कारण Python आपोआप पहिला argument म्हणून object स्वतः (self) पाठवतं. जर तुम्ही self लिहायला विसरलात, तर Python ला वाटतं की तुम्ही जास्तीचा argument दिला आहे. ही चूक जवळपास प्रत्येक नवशिक्या करतो — त्यामुळे लक्षात ठेवा: पहिला parameter नेहमी self असतो

Object हा class चा actual instance आहे. म्हणजे class हा फक्त नकाशा होता, आणि object म्हणजे त्या नकाशावरून प्रत्यक्ष बांधलेलं घर.

s1 = Student("Amit", 78)
s2 = Student("Sneha", 32)

print(s1.result())
print(s2.result())

Amit पास झाला/झाली आहे.
Sneha नापास झाला/झाली आहे.

लक्षात घ्या.s1 आणि s2 हे दोन वेगळे objects आहेत. प्रत्येकाचा स्वतःचा name आणि marks आहे, पण दोघेही एकाच Student class वरून बनलेले आहेत.

एका object कडे तीन गोष्टी असतात:

  • State: attributes मधून दिसणारा data (उदा. name, marks)
  • Behavior: methods मधून दिसणारं वर्तन (उदा. result())
  • Identity: प्रत्येक object हा वेगळा असतो, जरी data सारखा असला तरी

1. Inheritance (वारसा):

Inheritance म्हणजे एक class दुसऱ्या class चे गुणधर्म आणि methods घेऊ शकते. यामुळे code reuse होतो, तेच तेच लिहावं लागत नाही.

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

    def eat(self):
        print(f"{self.name} जेवत आहे.")

class Dog(Animal):     # Dog class Animal कडून inherit करतो
    def bark(self):
        print(f"{self.name} भुंकत आहे.")

d1 = Dog("Tommy")
d1.eat()     # Animal class ची method
d1.bark()    # Dog class ची स्वतःची method

Tommy जेवत आहे.
Tommy भुंकत आहे.

इथे Dog ला eat() परत लिहावं लागलं नाही, कारण ते आधीच Animal class मध्ये आहे. हेच खरं code reusability चं उदाहरण.

2. Polymorphism (बहुरूपता):

Polymorphism म्हणजे “एकच नाव, वेगवेगळं वर्तन.” म्हणजे एकाच method चं नाव वेगवेगळ्या classes मध्ये वेगळ्या पद्धतीने काम करू शकतं.

class Cat:
    def sound(self):
        print("मांजर 'म्याव' करते")

class Cow:
    def sound(self):
        print("गाय 'हंबरते'")

for animal in (Cat(), Cow()):
    animal.sound()

मांजर 'म्याव' करते
गाय 'हंबरते'

दोन्ही classes मध्ये sound() हे नाव सारखंच आहे, पण वर्तन वेगळं आहे, हेच polymorphism.

3. Encapsulation (संकलन)

Encapsulation म्हणजे data आणि त्यावर काम करणाऱ्या methods यांना एकत्र बांधून ठेवणं, आणि काही data बाहेरून directly बदलता येऊ नये यासाठी नियंत्रण ठेवणं.

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance     # double underscore = private

    def deposit(self, amount):
        self.__balance += amount

    def show_balance(self):
        print(f"सध्याची शिल्लक: {self.__balance}")

acc = BankAccount(1000)
acc.deposit(500)
acc.show_balance()

Output: सध्याची शिल्लक: 1500

माझी दुसरी चूक — private variable थेट access करण्याचा प्रयत्न मी सुरुवातीला असं लिहिलं होतं: print(acc.__balance)

आणि मला हा error मिळाला: AttributeError: ‘BankAccount’ object has no attribute ‘__balance’

याचं कारण असं की __balance सारखे double-underscore attributes Python मध्ये name mangling मुळे बाहेरून direct access करता येत नाहीत. हाच तर encapsulation चा उद्देश आहे — डेटा सुरक्षित ठेवणं, आणि तो फक्त class च्या आतल्या methods मधूनच बदलता यावा.

4. Data Abstraction (अमूर्तीकरण)

Abstraction म्हणजे “काय होतंय” हे दाखवणं, पण “कसं होतंय” ते लपवणं. उदाहरणार्थ, तुम्ही bike चालवताना फक्त accelerator आणि brake वापरता — इंजिन आतून कसं काम करतं हे तुम्हाला माहीत असण्याची गरज नसते.

from abc import ABC, abstractmethod

class Vehicle(ABC):
    @abstractmethod
    def start_engine(self):
        pass

class Bike(Vehicle):
    def start_engine(self):
        print("बाईकचं इंजिन सुरू झालं.")

b = Bike()
b.start_engine()

Output: बाईकचं इंजिन सुरू झालं.

खाली एक छोटं उदाहरण आहे जे मी स्वतः सराव करताना बनवलं होतं, Library Management चा एक छोटा भाग:

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author
        self.is_issued = False

    def issue_book(self):
        if not self.is_issued:
            self.is_issued = True
            print(f"'{self.title}' पुस्तक issue झालं.")
        else:
            print(f"'{self.title}' आधीच issue केलेलं आहे.")

b1 = Book("Python पूर्ण मार्गदर्शक", "Rupesh Pawar")
b1.issue_book()
b1.issue_book()

Output:

‘Python पूर्ण मार्गदर्शक’ पुस्तक issue झालं.
‘Python पूर्ण मार्गदर्शक’ आधीच issue केलेलं आहे.

हा एक साधा पण practical उदाहरण आहे जो दाखवतो की class, object, आणि methods एकत्र कसे काम करतात.

Inheritance शिकताना एक गोष्ट पुढे नक्की लागते, method overriding. म्हणजे child class ने parent class ची method स्वतःच्या पद्धतीने पुन्हा लिहिणं.

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

    def make_sound(self):
        print(f"{self.name} काहीतरी आवाज करतो.")

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)     # parent class चा constructor call
        self.breed = breed

    def make_sound(self):          # overriding
        print(f"{self.name} (जात: {self.breed}) भुंकतो.")

d = Dog("Bruno", "Labrador")
d.make_sound()

Output: Bruno (जात: Labrador) भुंकतो.

super() वापरल्यामुळे मला parent class चा __init__ पुन्हा लिहावा लागला नाही — फक्त नवीन गोष्ट (breed) जोडली. सुरुवातीला मी super().__init__(name) ऐवजी थेट Animal.__init__(self, name) असं लिहायचो — दोन्ही चालतं, पण super() वापरणं जास्त clean आणि maintainable मानलं जातं, विशेषतः जेव्हा multiple inheritance असते.

बऱ्याच tutorials मध्ये हे टाळलं जातं, पण practical कोड लिहिताना हे नक्की लागतं.

class Employee:
    company_name = "lgrow.in"

    def __init__(self, name):
        self.name = name

    @classmethod
    def change_company(cls, new_name):
        cls.company_name = new_name

    @staticmethod
    def is_valid_age(age):
        return age >= 18

print(Employee.is_valid_age(20))    # True
Employee.change_company("Lgrow Technologies")
print(Employee.company_name)        # Lgrow Technologies

Output: True
Lgrow Technologies

  • @classmethod — पहिला parameter cls घेतो, आणि class-level data बदलण्यासाठी वापरला जातो.
  • @staticmethod — ना self घेतो ना cls. जेव्हा एखादी utility function class च्या आत ठेवायची असते, पण त्याला object किंवा class च्या data ची गरज नसते, तेव्हा static method वापरतात.

मी सुरुवातीला या दोघांमध्ये गोंधळून जायचो — पण साधा नियम लक्षात ठेवा: जर तुम्हाला class चा data बदलायचा आहे, तर classmethod; जर फक्त logic हवं आहे data शिवाय, तर staticmethod.

बऱ्याच नवशिक्यांना (मला सुद्धा सुरुवातीला) हे दोन concepts सारखेच वाटतात — कारण दोघांचाही उद्देश “काहीतरी लपवणं” असा दिसतो. पण प्रत्यक्षात फरक असा आहे:

  • Encapsulation: कसं लपवायचं याबद्दल आहे. म्हणजे data आणि methods एकत्र बांधून, आणि access control (private/protected) वापरून डेटा सुरक्षित ठेवणं.
  • Abstraction: काय दाखवायचं याबद्दल आहे. म्हणजे वापरकर्त्याला फक्त गरजेचं interface दाखवणं, आतलं implementation लपवणं.

सोप्या शब्दांत, Encapsulation ही design ची पद्धत आहे (data कसा ठेवायचा), तर Abstraction ही design ची विचारसरणी आहे (काय दाखवायचं, काय नाही). ATM मशीनचं उदाहरण घेतलं तर — तुम्हाला फक्त “पैसे काढा” हे बटण दिसतं (abstraction), पण त्यामागचा बँकेचा database, security checks हे सगळं तुमच्यापासून लपवलेलं आणि सुरक्षित ठेवलेलं असतं (encapsulation).

Tutorial मध्ये उदाहरणं दिली की गोष्टी सोप्या वाटतात, पण खरं महत्त्व कळतं जेव्हा तुम्ही मोठे प्रोजेक्ट्स बघता:

  • Django/Flask web development — प्रत्येक database table हा एक class असतो (Models), आणि प्रत्येक row हा एक object.
  • Game development — प्रत्येक player, enemy, item हे एकेक class म्हणून तयार केले जातात.
  • Automation scripts — उदा. WordPress साठी टूल्स बनवताना, प्रत्येक “task” (उदा. Error Decoder, Translator) एक class म्हणून organize केल्यास कोड जास्त readable राहतो.

lgrow.in वरचे इंटरॅक्टिव्ह टूल्स बनवतानाही मी हीच पद्धत वापरतो — प्रत्येक टूलचं लॉजिक वेगळ्या class मध्ये ठेवलं की debug करणं आणि नवीन features जोडणं खूप सोपं होतं.

जर तुम्ही placement किंवा internship साठी तयारी करत असाल, तर खालील प्रश्न वारंवार विचारले जातात, मला स्वतः यापैकी दोन प्रश्न एका मुलाखतीत आले होते, आणि तेव्हा मला जाणवलं की फक्त definitions पाठ करून उपयोग नाही, तर प्रत्यक्ष कोड लिहून concept समजणं गरजेचं आहे.

1. Class आणि Structure (इतर भाषांमधलं) यात फरक काय?

Ans: Class मध्ये data बरोबर methods (behavior) सुद्धा असतात, तर struct मध्ये फक्त data असतो.

2. __init__ आणि __new__ यात फरक काय?

Ans: __new__ object प्रत्यक्ष तयार करतो (memory allocate करतो), तर __init__ तो object तयार झाल्यावर त्याचा data initialize करतो. बहुतांश वेळा फक्त __init__ सोबतच काम पुरतं.

3. Method Overloading Python मध्ये शक्य आहे का?

Ans: इतर भाषांप्रमाणे (Java/C++) थेट overloading Python मध्ये नाही, पण default parameters किंवा *args, **kwargs वापरून तसंच वर्तन मिळवता येतं.

4. Composition आणि Inheritance मध्ये फरक काय?

Ans: — Inheritance म्हणजे “is-a” relationship (उदा. Dog is an Animal), तर Composition म्हणजे “has-a” relationship (उदा. Car has an Engine — engine ही स्वतंत्र class असून Car मध्ये object म्हणून वापरली जाते).

Composition चं एक छोटं उदाहरण:

class Engine:
    def start(self):
        print("इंजिन सुरू झालं.")

class Car:
    def __init__(self):
        self.engine = Engine()   # Composition — Car कडे Engine चं object आहे

    def start_car(self):
        self.engine.start()
        print("गाडी सुरू झाली.")

my_car = Car()
my_car.start_car()

Output: इंजिन सुरू झालं.
गाडी सुरू झाली.

बऱ्याचदा नवशिक्या फक्त Inheritance शिकून थांबतात, पण real projects मध्ये Composition जास्त वापरली जाते — कारण ती flexible असते आणि tightly-coupled classes टाळते.

Q1. Class आणि Object मध्ये नेमका फरक काय?

Ans: Class हा एक blueprint/साचा आहे, तर Object हा त्या साच्यावरून तयार झालेली प्रत्यक्ष वस्तू आहे. एका class पासून अनेक objects बनवता येतात.

Q2. self का लागतो, आणि तो का लिहायला विसरला जातो?

Ans: self म्हणजे “हा विशिष्ट object.” प्रत्येक instance method मध्ये पहिला parameter म्हणून तो लागतोच, कारण Python ला कळायला हवं की कोणत्या object वर काम करायचं आहे. सुरुवातीला विसरला जातो कारण तो implicitly (आपोआप) पास होतो, त्यामुळे तो “दिसत” नाही.

Q3. Encapsulation खरंच data पूर्णपणे लपवतं का?

Ans: नाही, पूर्णपणे नाही. Python मध्ये खरी “private” संकल्पना नाही — __balance सारखे attributes फक्त name-mangling मुळे थेट access करणं अवघड होतं, पण _ClassName__balance असं लिहून तांत्रिकदृष्ट्या access करता येतं. हे फक्त एक convention आणि best-practice आहे, कडक सुरक्षा नाही.

Q4. Multiple inheritance म्हणजे काय, आणि ती वापरावी का?

Ans: एक class एकापेक्षा जास्त parent classes कडून inherit करू शकतो. उदा. class C(A, B):. ही सुविधा आहे, पण जास्त वापरल्यास कोड गुंतागुंतीचा होतो (यालाच “Diamond Problem” म्हणतात) — त्यामुळे गरज असेल तेव्हाच वापरावी.

Blogger Vinita

Blogger Rupesh

माझं नाव रुपेश आहे, आणि मी एक Blogger तसेच Content Writer आहे. मी माझा ब्लॉगिंगचा प्रवास वयाच्या ३० व्या वर्षी सुरू केला आणि आज मला या क्षेत्रात ५ वर्षांचा अनुभव आहे.माझा ब्लॉग “Learn Grow” मराठी भाषेत असून, त्यावर मी Blogging, Education, Programming शिकणे आणि AI Tools यांसारख्या महत्त्वाच्या विषयांवर सोप्या आणि समजण्यासारख्या भाषेत माहिती शेअर करतो. यासोबतच, मी Freelancing Services देखील प्रदान करतो, ज्यामध्ये Content Writing, SEO आणि Digital Marketing संबंधित कामांचा समावेश आहे.

Scroll to Top