# Python-இல் வகுப்புகளும் OOP-உம்: __init__, self, முறைகள், மரபுரிமை மற்றும் dataclass

URL: https://skillaria.top/ta/guides/python/klassy-python

Python OOP வகுப்புகளை அடிப்படையாகக் கொண்டது: வகுப்பு புலங்களையும் முறைகளையும் விவரிக்கிறது, பொருள்கள் (நிகழ்வுகள்) வகுப்பை அழைப்பதன் மூலம் உருவாகின்றன; __init__ புலங்களை நிரப்புகிறது, self தற்போதைய பொருளைச் சுட்டுகிறது. மரபுரிமை பெற்றோரின் நடத்தையை எடுத்து சில முறைகளை மாற்ற அனுமதிக்கிறது; @dataclass தரவைச் சேமிக்கும் வகுப்புகளுக்கு __init__, __repr__ மற்றும் __eq__ ஐத் தானாக உருவாக்குகிறது.

Python-இல் வகுப்பு ஒரு வரைவுத் திட்டம் (blueprint): அது பொருள் எந்தத் தரவைச் சேமிக்கும், அதனுடன் என்ன செய்ய முடியும் என்று விவரிக்கிறது. பொருள் (நிகழ்வு) அந்தத் திட்டத்தின்படி செய்யப்பட்ட குறிப்பிட்ட பொருள். Python OOP மூன்று விஷயங்களின் மேல் நிற்கிறது: முறைகளுடன் கூடிய வகுப்புகள், மரபுரிமை, மற்றும் `__init__`, `__repr__` போன்ற சிறப்பு முறைகள். ஒரே எடுத்துக்காட்டில் — செலவுக் கணக்கு — எல்லாவற்றையும் வரிசையாகப் பார்ப்போம்.

## அகராதிகள் இருக்கும்போது வகுப்புகள் எதற்கு

தரவை அகராதியில் சேமிக்கலாம்: `{"title": "காபி", "amount": 250}`. பிரச்சனை என்னவென்றால், அகராதிக்கு எந்த விசைகள் சரி என்று தெரியாது. `expense.get("amout", 0)` என்ற எழுத்துப்பிழை பிழை தராது — பூஜ்ஜியம் திரும்பும், அறிக்கை அமைதியாகப் பொய் சொல்லத் தொடங்கும்.

வகுப்புப் பொருளின் புலங்கள் முன்கூட்டியே அறிவிக்கப்பட்டவை. இல்லாத பண்புக்கூறை அணுகினால் உடனே `AttributeError` வரும்; நவீன Python சரியான பெயரையும் பரிந்துரைக்கும். மேலும் வகுப்பு தரவையும் அதன் மீதான செயல்களையும் ஒரே இடத்தில் வைத்திருக்கிறது: செலவு தானே வரியுடன் தொகையைக் கணக்கிடும், அறிக்கை தானே மொத்தத்தைக் கணக்கிடும்.

## Python-இல் வகுப்பை உருவாக்குவது

வகுப்பு `class` சொல்லால் அறிவிக்கப்படுகிறது; பெயர் PEP 8 மரபின்படி CamelCase-இல். எளிய வகுப்பு எடுத்துக்காட்டு:

```
class Expense:
    def __init__(self, title, amount):
        self.title = title
        self.amount = amount

coffee = Expense("காபி", 250)
taxi = Expense("டாக்ஸி", 300)
print(coffee.title, coffee.amount)   # காபி 250
```

`Expense("காபி", 250)` அழைப்பு ஒரு புதிய நிகழ்வை உருவாக்குகிறது. `coffee` மற்றும் `taxi` ஒரே வகையின் இரண்டு வெவ்வேறு பொருள்கள், ஒவ்வொன்றுக்கும் தனித்தனி புல மதிப்புகள். பொருள் இந்த வகுப்பைச் சேர்ந்ததா என்று `isinstance(coffee, Expense)` சரிபார்க்கலாம் — `True` தரும்.

### __init__ தொடக்க முறை

`__init__`-ஐ பெரும்பாலும் வகுப்பின் கன்ஸ்ட்ரக்டர் என்று அழைக்கிறோம், ஆனால் துல்லியமாகச் சொன்னால் அது துவக்கி (initializer). பொருளை உருவாக்குவது `__new__` முறை; `__init__` அதன் பின்னர் உடனே அழைக்கப்பட்டு, கொடுக்கப்பட்ட வாதங்களை புலங்களில் வைக்கிறது. அதிலிருந்து எதையும் திருப்ப வேண்டியதில்லை (துல்லியமாகச் சொன்னால் அது `None` தர வேண்டும்).

`__init__`-இல் உள்ளீட்டுத் தரவைச் சரிபார்ப்பது வசதியானது; அப்போது நிரலில் தவறான பொருள்கள் உருவாகாது:

```
class Expense:
    def __init__(self, title, amount):
        if amount < 0:
            raise ValueError("தொகை எதிர்மறையாக இருக்க முடியாது")
        self.title = title
        self.amount = amount
```

### Python-இல் self என்றால் என்ன

`self` என்பது முறை தற்போது வேலை செய்யும் அதே பொருளின் குறிப்பு. `coffee.with_tax()` என்று எழுதும்போது Python உண்மையில் `Expense.with_tax(coffee)` என்று அழைத்து பொருளை முதல் வாதமாக அனுப்புகிறது. அதனால் முறையின் அறிவிப்பில் `self` முதலில் வரும்; அழைக்கும்போது அது எழுதப்படுவதில்லை.

`self` ஒரு சிறப்புச் சொல் அல்ல, ஒரு மரபு: தொழில்நுட்ப ரீதியாக அளவுருவை எந்தப் பெயரிலும் வைக்கலாம், ஆனால் யாரும் அப்படிச் செய்வதில்லை. தொடக்கநிலையாளரின் பொதுவான தவறு — முறையின் அறிவிப்பில் `self`-ஐ மறப்பது. அப்போது அழைப்பு `TypeError: Expense.with_tax() takes 0 positional arguments but 1 was given` உடன் விழும்.

## முறைகளும் சிறப்பு முறைகளும்

முறை என்பது வகுப்புக்குள் அறிவிக்கப்பட்ட செயல்பாடு. அது `self` மூலம் பொருளின் புலங்களைப் பார்க்கும்:

```
class Expense:
    def __init__(self, title, amount):
        self.title = title
        self.amount = amount

    def with_tax(self, rate=0.2):
        return self.amount * (1 + rate)

    def __repr__(self):
        return f"Expense({self.title!r}, {self.amount})"

coffee = Expense("காபி", 250)
print(coffee.with_tax())   # 300.0
print(coffee)              # Expense('காபி', 250)
```

`__repr__` இல்லையென்றால் வெளியீடு `<__main__.Expense object at 0x7f...>` போல இருக்கும் — பிழைதிருத்தத்துக்கு பயனற்றது. இருபுறமும் இரட்டை அடிக்கோடுகள் உள்ள முறைகளை சிறப்பு (dunder) முறைகள் என்கிறோம்: அவற்றை Python தானே அழைக்கும். `__str__` பயனருக்கான அழகான வெளியீட்டைக் கவனிக்கும், `__eq__` `==` ஒப்பீட்டை, `__len__` `len(obj)`-ஐ.

### @classmethod மற்றும் @staticmethod

சாதாரண முறை பொருளுடன் வேலை செய்கிறது. `@classmethod` அலங்கரிக்கப்பட்ட முறை முதல் வாதமாக வகுப்பையே (`cls`) பெறுகிறது — அதை மாற்றுக் கன்ஸ்ட்ரக்டராகப் பயன்படுத்துவது வழக்கம். `@staticmethod` எந்தப் பொருளையும் வகுப்பையும் பெறுவதில்லை: அது வகுப்புடன் தர்க்கரீதியாகத் தொடர்புடைய சாதாரண செயல்பாடு.

```
class Expense:
    def __init__(self, title, amount):
        self.title = title
        self.amount = amount

    @classmethod
    def from_csv(cls, line):
        title, amount = line.split(";")
        return cls(title, float(amount))

    @staticmethod
    def is_valid_amount(value):
        return value >= 0

e = Expense.from_csv("மதிய உணவு;520")
print(e.amount)                    # 520.0
print(Expense.is_valid_amount(-1)) # False
```

### வகுப்பின் அழிப்பான்: __del__

Python-இல் `__del__` முறை உண்டு; குப்பை சேகரிப்பான் பொருளை அழிப்பதற்கு முன் அது அழைக்கப்படுகிறது. வளங்களை விடுவிக்க அதை நம்ப வேண்டாம்: அழைக்கப்படும் நேரம் உத்தரவாதமற்றது. கோப்புகளுக்கும் இணைப்புகளுக்கும் `with` சூழல் மேலாளரையோ வெளிப்படையான `close()` முறையையோ பயன்படுத்துங்கள்.

## வகுப்பின் பண்புக்கூறுகளும் பொருளின் பண்புக்கூறுகளும்

`self.name = ...` மூலம் அமைக்கப்படும் புலங்கள் குறிப்பிட்ட பொருளுக்குச் சொந்தமானவை. வகுப்பின் உடலில் நேரடியாக அறிவிக்கப்பட்ட மாறிகள் வகுப்பின் பண்புக்கூறுகள்: அவை எல்லா நிகழ்வுகளுக்கும் பொதுவானவை. வகுப்பின் மாறிலிகளை இப்படித்தான் வைப்பார்கள்.

```
class Expense:
    CURRENCY = "INR"          # வகுப்பு பண்புக்கூறு, மாறிலி

    def __init__(self, title, amount):
        self.title = title    # பொருளின் பண்புக்கூறுகள்
        self.amount = amount

a = Expense("காபி", 250)
b = Expense("தேநீர்", 150)
print(a.CURRENCY, Expense.CURRENCY)   # INR INR
```

ஆபத்தான பொறி — மாற்றக்கூடிய வகுப்புப் பண்புக்கூறு. வகுப்பின் உடலில் `rows = []` என்று அறிவித்து `self.rows.append(...)` மூலம் சேர்த்தால், அந்தப் பட்டியல் எல்லாப் பொருள்களுக்கும் பொதுவாகிவிடும். மாற்றக்கூடிய புலங்களை எப்போதும் `__init__`-இல் உருவாக்குங்கள்.

பொருளின் புலங்களை `vars(obj)` அல்லது `obj.__dict__` மூலம் பார்க்கலாம்; சரத்திலிருந்து பெயரால் பண்புக்கூறைப் படிக்க — `getattr(obj, "amount")`.

## வகுப்பின் பண்புகள்: @property

பண்பு (property) என்பது வெளியிலிருந்து அடைப்புகள் இல்லாமல் சாதாரண புலம் போலப் படிக்கப்படும் முறை. கணக்கிடப்படும் மதிப்புகளுக்கு இது வசதியானது: அறிக்கையின் மொத்தத்தைத் தனியாகச் சேமிக்கத் தேவையில்லை, அதைப் புதுப்பிக்க மறக்கவும் மாட்டீர்கள்.

```
class Report:
    def __init__(self):
        self.rows = []

    def add(self, expense):
        self.rows.append(expense)

    @property
    def total(self):
        return sum(e.amount for e in self.rows)

r = Report()
r.add(Expense("காபி", 250))
r.add(Expense("டாக்ஸி", 300))
print(r.total)   # 550
```

எழுதுவதைக் கட்டுப்படுத்த வேண்டுமென்றால் `@total.setter` மூலம் setter சேர்க்கலாம். Setter இல்லாமல் `r.total = 0` என்று ஒதுக்கினால் `AttributeError` வரும்.

உட்பொதித்தல் (encapsulation) பற்றி: Python-இல் கண்டிப்பான அர்த்தத்தில் தனிப்பட்ட புலங்கள் இல்லை. ஒரு அடிக்கோடு (`_cache`) "வெளியிலிருந்து தொடாதீர்கள்" என்ற ஒப்பந்தம்; இரண்டு அடிக்கோடுகள் (`__secret`) பெயர் திரிபை (name mangling) இயக்குகின்றன — வாரிசு தற்செயலாக அந்தப் புலத்தை மேலெழுதாமல் இருக்க.

## Python-இல் வகுப்பு மரபுரிமை

மரபுரிமை ஒரு பெற்றோர் வகுப்பின் அடிப்படையில் குழந்தை வகுப்பை உருவாக்க அனுமதிக்கிறது: வாரிசு எல்லா புலங்களையும் முறைகளையும் பெறும், தேவையானவற்றை மேலெழுதும். பெற்றோர் வகுப்பு அடைப்புக்குள் குறிக்கப்படும்.

```
class MonthlyReport(Report):
    def __init__(self, month):
        super().__init__()        # பெற்றோரின் __init__-ஐ அழை
        self.month = month

    def add(self, expense):       # முறையை மேலெழுதுதல்
        print(f"[{self.month}] + {expense}")
        super().add(expense)      # பெற்றோரின் நடத்தையைத் தக்கவை

m = MonthlyReport("அக்டோபர்")
m.add(Expense("காபி", 250))   # [அக்டோபர்] + Expense('காபி', 250)
print(m.total)                # 250 — பண்பு மரபுரிமையாகக் கிடைத்தது
```

`super()` பெற்றோரின் முறைகளை அழைப்பதற்கான இடைநிலைப் பொருளைத் தரும். குழந்தையின் `__init__`-இல் `super().__init__()`-ஐ மறப்பது உன்னதப் பிழை: `rows` புலம் உருவாகாது, முதல் `add` `AttributeError`-உடன் விழும்.

உறவைச் சரிபார்க்க `issubclass(MonthlyReport, Report)`. Python 3-இல் எல்லா வகுப்புகளும் மறைமுகமாக `object`-இலிருந்து மரபுரிமை பெறுகின்றன — அதனால்தான் அடிப்படை `__repr__`, `__eq__` போன்ற முறைகள் வருகின்றன. Python பல மரபுரிமையையும் (`class C(A, B)`) ஆதரிக்கிறது; முறைகள் தேடப்படும் வரிசையை `C.__mro__` காட்டும்.

### Python-இல் சுருக்க வகுப்பு (abstract class)

சுருக்க வகுப்பு ஒரு இடைமுகத்தை வரையறுக்கிறது: வாரிசுகள் எந்த முறைகளைச் செயல்படுத்த வேண்டும். அதன் பொருளை உருவாக்க முடியாது. இதற்கு `abc` தொகுதி:

```
from abc import ABC, abstractmethod

class Exporter(ABC):
    @abstractmethod
    def export(self, report): ...

class CsvExporter(Exporter):
    def export(self, report):
        return "\n".join(f"{e.title};{e.amount}" for e in report.rows)

Exporter()   # TypeError: Can't instantiate abstract class Exporter ...
```

எல்லா சுருக்க முறைகளையும் செயல்படுத்தாத வாரிசும் சுருக்கமாகவே இருக்கும்; அதன் பொருளை உருவாக்கினாலும் அதே பிழை வரும்.

## ஆழமான படிநிலைகளுக்குப் பதிலாக கூட்டமைப்பு (composition)

மரபுரிமை பற்றி அறிந்ததும் படிநிலைகளைக் கட்ட ஆசை வரும்: அடிப்படைச் செலவு, பண-செலவு, ரூபாய்-செலவு — இப்படி ஐந்து நிலைகள் வரை. Python-இல் இது அரிதாகவே எழுதப்படுகிறது. மரபுரிமை "என்பது" உறவை விவரிக்கிறது; கூட்டமைப்பு "கொண்டுள்ளது" உறவை: அறிக்கை செலவுகளின் பட்டியல் அல்ல, அதை `rows` புலத்தில் கொண்டுள்ளது.

நடைமுறை விதி: வேறொரு பொருளின் திறனை மட்டும் பயன்படுத்த வேண்டுமென்றால் அதை ஒரு புலமாக வையுங்கள், மரபுரிமை பெறாதீர்கள். தரவுக்கு பெரும்பாலும் dataclass போதும்; தர்க்கத்தை சாதாரண செயல்பாடுகளில் வைக்கலாம்.

## Python dataclass: திரும்பத் திரும்பக் குறியீடு இல்லாத வகுப்பு

முக்கியமாகத் தரவைச் சேமிக்கும் வகுப்புகளுக்கு `__init__`, `__repr__` மற்றும் `__eq__`-ஐ கைமுறையாக எழுதுவது சோர்வூட்டும். `dataclasses` தொகுதியின் `@dataclass` அலங்காரி அவற்றை புலங்களின் குறிப்புகளிலிருந்து உருவாக்குகிறது:

```
from dataclasses import dataclass, field

@dataclass
class Expense:
    title: str
    amount: float
    tags: list[str] = field(default_factory=list)

    def __post_init__(self):
        if self.amount < 0:
            raise ValueError("தொகை எதிர்மறையாக இருக்க முடியாது")

e = Expense("காபி", 250)
print(e)                           # Expense(title='காபி', amount=250, tags=[])
print(e == Expense("காபி", 250))   # True
```

`__init__`-இல் முன்பு எழுதிய மதிப்பு சரிபார்ப்பு இப்போது `__post_init__`-இல் செல்கிறது — அது உருவாக்கப்பட்ட துவக்கிக்குப் பின் உடனே அழைக்கப்படுகிறது. மாற்றக்கூடிய இயல்பு மதிப்புகளை `field(default_factory=list)` மூலம் மட்டுமே தர வேண்டும்: `tags: list = []` என்பதை dataclass தடுத்து `ValueError` தரும்.

### frozen மற்றும் slots

`frozen=True` பொருளை மாற்ற முடியாததாக்குகிறது; `slots=True` (Python 3.10 முதல்) `__slots__`-ஐ உருவாக்கி நினைவகத்தை மிச்சப்படுத்துகிறது. உறைந்த பொருளின் புலத்தை மாற்ற முயன்றால் `FrozenInstanceError` வரும். புதிய மதிப்புடன் பொருள் வேண்டுமென்றால் நகலை உருவாக்குங்கள்:

```
from dataclasses import dataclass, replace

@dataclass(frozen=True, slots=True)
class Expense:
    title: str
    amount: float

e = Expense("காபி", 250)
# e.amount = 300   -> dataclasses.FrozenInstanceError
e2 = replace(e, amount=300)
print(e, e2)   # Expense(title='காபி', amount=250) Expense(title='காபி', amount=300)
```

Python 3.13-இலிருந்து அதே வேலையை பொதுவான `copy.replace()` செயல்பாடும் செய்யும். உறைந்த dataclass பொருள்களை set-களில் வைக்கலாம், அகராதியின் விசைகளாகவும் பயன்படுத்தலாம் — அவற்றுக்கு `__hash__` உண்டு.

## வகுப்புகளில் புதியவை: Self, @override, __slots__

வகுப்புகளின் வகை குறிப்புக்கு இரண்டு பயனுள்ள கருவிகள் உள்ளன. `typing.Self` (Python 3.11, PEP 673) "அதே வகுப்பின் பொருளே திரும்பும்" என்று பொருள்; இது மரபுரிமை பெற்ற முறைகளிலும் வேலை செய்கிறது. `@override` அலங்காரி (Python 3.12, PEP 698) பெற்றோரின் முறையை மேலெழுத வேண்டிய முறையைக் குறிக்கிறது: பெயரில் எழுத்துப்பிழை இருந்தால் mypy அல்லது pyright அப்படிப்பட்ட அடிப்படை முறை இல்லை என்று சொல்லும்.

```
from typing import Self, override

class Report:
    def __init__(self):
        self.rows = []

    def add(self, e) -> Self:
        self.rows.append(e)
        return self              # சங்கிலியாக அழைக்கலாம்

class MonthlyReport(Report):
    @override
    def add(self, e) -> Self:
        return super().add(e)
```

Python 3.15-இல் ஒரு குறுகிய கட்டுப்பாடு நீக்கப்பட்டது: `tuple`-இன் துணைவகுப்புகளுக்கு காலியல்லாத `__slots__` அனுமதிக்கப்படுகிறது, எந்த வகுப்பின் `__slots__`-இலும் `__dict__` மற்றும் `__weakref__` குறிப்பிடலாம். 3.14-இல் tuple வாரிசுக்கு அத்தகைய அறிவிப்பு `TypeError: nonempty __slots__ not supported for subtype of 'tuple'`-உடன் விழுந்தது.

## வகுப்புகளில் பொதுவான தவறுகள்

| பிழை | அறிகுறி | எப்படி சரிசெய்வது |
| --- | --- | --- |

| முறையில் `self` மறந்தது | `TypeError: ... takes 0 positional arguments but 1 was given` | முறையின் முதல் அளவுரு எப்போதும் `self` |

| `__init__`-இல் `self.` இல்லாத புலம் | மதிப்பு `__init__`-க்குப் பின் மறைகிறது | `self.amount = amount` என்று எழுதுங்கள் |

| வகுப்புப் பண்பாக பட்டியல் | தரவு பொருள்களுக்கிடையே "கசிகிறது" | பட்டியல்களை `__init__`-இலோ `default_factory`-இலோ உருவாக்குங்கள் |

| `super().__init__()` இல்லை | பெற்றோரின் புலங்களில் `AttributeError` | பெற்றோரின் துவக்கியை அழையுங்கள் |

| பண்பை அடைப்புகளுடன் அழைப்பது | `TypeError: 'int' object is not callable` | பண்பை அடைப்புகள் இல்லாமல் படியுங்கள்: `r.total` |

## சுருக்கம்

Python OOP எளிமையாகத் தொடங்குகிறது: வகுப்பு புலங்களையும் முறைகளையும் விவரிக்கிறது, `__init__` புலங்களை நிரப்புகிறது, `self` தற்போதைய பொருளைச் சுட்டுகிறது. மரபுரிமையும் `super()`-உம் நடத்தையை விரிவாக்க அனுமதிக்கின்றன, சுருக்க வகுப்புகள் இடைமுகத்தை வரையறுக்கின்றன, `@property` கணக்கிடப்படும் புலங்களைத் தருகிறது. தரவு வகுப்புகளுக்கு `@dataclass`-ஐ எடுங்கள், ஆழமான படிநிலைகளுக்குப் பதிலாக கூட்டமைப்பைத் தேர்ந்தெடுங்கள். வகுப்புகளும் dataclass-களும் Python பாடநெறியில் செலவுக் கணக்கு எடுத்துக்காட்டில் நடைமுறையில் விளக்கப்பட்டுள்ளன.

## FAQ

**வகுப்புக்கும் பொருளுக்கும் Python-இல் என்ன வேறுபாடு?**

வகுப்பு வகையின் விவரம்: அதற்கு என்ன புலங்களும் முறைகளும் உள்ளன. பொருள், அல்லது நிகழ்வு, வகுப்பை அழைப்பதால் உருவாகிறது, உதாரணமாக Expense("காபி", 250), அது குறிப்பிட்ட புல மதிப்புகளை வைத்திருக்கும்.

**வகுப்பில் __init__ கட்டாயமா?**

இல்லை. __init__ அறிவிக்கப்படாவிட்டால் object-இலிருந்து வரும் ஒன்று பயன்படுத்தப்படும்; அது எந்த வாதங்களையும் ஏற்காது. dataclass-இல் அதை அலங்காரி உருவாக்குகிறது.

**__str__ மற்றும் __repr__ இடையே வேறுபாடு என்ன?**

__repr__ டெவலப்பருக்கும் பிழைதிருத்திக்கும் ஒரே பொருளைக் காட்டும் வடிவம்; __str__ print மற்றும் f-சரங்களில் பயனருக்கான தெளிவான வெளியீடு. __str__ இல்லையென்றால் Python __repr__-ஐப் பயன்படுத்தும்.

**dataclass எப்போது, சாதாரண வகுப்பு எப்போது?**

வகுப்பு முக்கியமாகத் தரவைச் சேமித்து, தயார் __init__, __repr__ மற்றும் ஒப்பீடு தேவைப்படும்போது dataclass. துவக்கத்தில் சிக்கலான தர்க்கம் இருந்தாலோ நடத்தை தரவை விட முக்கியமாக இருந்தாலோ சாதாரண வகுப்பு வசதியானது.

**Python-இல் தனிப்பட்ட புலங்கள் உண்டா?**

கண்டிப்பான தனியுரிமை இல்லை. பெயரின் தொடக்கத்தில் ஒரு அடிக்கோடு "வெளி பயன்பாட்டுக்கு அல்ல" என்ற ஒப்பந்தம்; இரண்டு அடிக்கோடுகள் பெயர் திரிபை இயக்குகின்றன, அது வாரிசில் தற்செயலான மேலெழுதலிலிருந்து புலத்தைக் காக்கிறது.


---
Source: Skillaria.Top (https://skillaria.top)
