The @property decorator in Python is used to make a method behave like an attribute. It allows you to define getters, setters, and deleters in an elegant and Pythonic way.
1. Why use @property?
- Normally, in object-oriented programming, you hide internal data and access it using
get_andset_methods.
Example without @property:
class Person:
def __init__(self, age):
self._age = age
def get_age(self): # Getter
return self._age
def set_age(self, value): # Setter
if 0 <= value <= 150:
self._age = value
else:
print("Invalid age!")
p = Person(30)
print(p.get_age()) # 30
p.set_age(40) # Update age
print(p.get_age()) # 40
Problem: Calling get_age() and set_age() every time is not Pythonic.
2. Using @property (Pythonic way)
class Person:
def __init__(self, age):
self._age = age # Internal storage
@property
def age(self): # Getter
return self._age
@age.setter
def age(self, value): # Setter with validation
if 0 <= value <= 150:
self._age = value
else:
print("Invalid age!")
Usage:
p = Person(30)
print(p.age) # ✅ Looks like attribute access (calls getter)
p.age = 40 # ✅ Looks like attribute assignment (calls setter)
p.age = -5 # Prints "Invalid age!"
Output:
30
40
Invalid age!
3. How @property Works Internally
@propertyconverts theage()method into a getter property.@age.setterlinks a setter method to the same property name.- Accessing
p.age→ Calls the getter. - Assigning
p.age = value→ Calls the setter.
4. Advantages
- Cleaner syntax – Access attributes like normal variables.
- Encapsulation – Control how attributes are read/written.
- Validation – Add checks before updating a value.
- Backward compatibility – You can add properties later without breaking existing code.
5. Full Property Example
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self): # Getter
return self._radius
@radius.setter
def radius(self, r): # Setter
if r > 0:
self._radius = r
else:
print("Radius must be positive!")
@property
def area(self): # Read-only property
return 3.14 * (self._radius ** 2)
c = Circle(5)
print(c.radius) # 5
c.radius = 10 # Updates radius
print(c.area) # 314.0 (computed dynamically)
c.radius = -3 # Invalid
Key Points
@property= Getter@<property>.setter= Setter@<property>.deleter= Optional, to delete the attribute- Makes your class clean, safe, and Pythonic.
Access Modifiers
Python में Access Modifiers का concept होता है, लेकिन ये Java / C++ जैसे strict नहीं होते।
Python में कोई private, public, protected keyword नहीं होते।
इसके बदले हम naming conventions और name mangling पर depend करते हैं।
1. Types of Access Modifiers in Python
Python में 3 प्रकार के Access Levels follow किए जाते हैं:
| Type | Convention | Example | Access Rule |
|---|---|---|---|
| Public | कोई underscore नहीं | self.age | कहीं से भी access कर सकते हैं |
| Protected | Single _ | self._age | Convention: "Internal use only", पर फिर भी access कर सकते हैं |
| Private | Double __ | self.__age | Name Mangling होता है, accidental access से बचाता है |
2. Public Attributes (Default)
- Python में default सब कुछ public होता है।
- कहीं से भी access कर सकते हैं।
class Person:
def __init__(self):
self.age = 30 # Public
p = Person()
print(p.age) # ✅ 30
p.age = 40 # ✅ Update allowed
3. Protected Attributes (_)
- Single underscore
_conventionally बताता है कि यह internal use के लिए है। - Python इसे रोकता नहीं है, सिर्फ developers को hint देता है।
class Person:
def __init__(self):
self._age = 30 # Protected
p = Person()
print(p._age) # ⚠️ Possible but not recommended (30)
p._age = 40 # ⚠️ Possible but not recommended
print(p._age)
4. Private Attributes (__)
- Double underscore
__से शुरू होने वाले attributes name mangling use करते हैं। - Python internally attribute का नाम
_ClassName__AttributeNameकर देता है।
class Person:
def __init__(self):
self.__age = 30 # Private
p = Person()
print(p.__age) # ❌ AttributeError
print(p._Person__age) # ✅ 30 (Name mangled access)
- इसका use accidental access से बचाने के लिए होता है।
- फिर भी आप चाहे तो
_ClassName__varलिखकर access कर सकते हैं।
Python philosophy:
“We are all consenting adults here.”
मतलब Python में strict restriction नहीं होती, devs पर trust किया जाता है कि वो rules follow करेंगे।
Decorators in Python
What is a Decorator?
- A decorator is like a helper that adds extra features to a function without changing its code.
- It takes a function as input and gives back a new function with extra work.
def my_decorator(func):
def wrapper():
print("Before function")
func()
print("After function")
return wrapper
def say_hello():
print("Hello!")
say_hello()
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# Decorator with Function Arguments
def smart_divide(func):
def wrapper(a, b):
if b == 0:
print("❌ Cannot divide by zero")
return
return func(a, b)
return wrapper
@smart_divide
def divide(a, b):
print(a / b)
divide(10, 2) # 5.0
divide(5, 0) # ❌ Cannot divide by zero
Python में *args और **kwargs इसलिए use करते हैं ताकि decorator हर type के function handle कर सके, चाहे उसमें कितने भी arguments हों।
def demo(*args):
print(args)
demo(1, 2, 3) # output of tuple: (1, 2, 3)
def demo(**kwargs ):
print(kwargs)
demo(name="Himanshu", age=25) # output of dict: {'name': 'Himanshu', 'age': 25}
Multiple Decorators
def uppercase(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs).upper()
return wrapper
def exclaim(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs) + "!!!"
return wrapper
@exclaim
@uppercase
def greet():
return "hello"
print(greet()) # HELLO!!!
Class Decorators
Decorators can also be applied to classes:
def add_repr(cls):
cls.__repr__ = lambda self: f"{self.__class__.__name__}({self.__dict__})"
return cls
@add_repr
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Himanshu", 25)
print(p) # Person({'name': 'Himanshu', 'age': 25})
Summary
Common Uses of Decorators:
- Logging – Keep track of when a function runs and what arguments it got.
Example: Record each time someone calls your function. - Timing – Measure how long a function takes to finish.
Example: Check performance of slow functions. - Authentication & Authorization – Verify if the user is allowed to use a function.
Example: Only admin can delete data. - Caching – Save the result of a function so next time it’s faster.
Example: Don’t calculate the same thing again and again. - Rate Limiting – Control how many times a function can be used.
Example: Prevent a user from clicking a button too often. - Input Validation – Automatically check if the input values are correct.
Example: Ensure age is a positive number before running the function. - Instrumentation / Monitoring – Collect data about function usage.
Example: Count how many times a function is used or check its performance.
Frameworks like Flask and Django use decorators extensively for routing, authentication, and defining middleware.