Dynamic Language Fundamentals
Python belongs to the family of dynamic programming languages—high-level languages that allow structural modifications during runtime. This category includes JavaScript, PHP, Ruby, and Python itself, while C and C++ represent static alternatives. Dynamic languages enable runtime code alterations: adding functions, objects, or entire code blocks, and removing existing functions.
Runtime Attribute Binding
Instance-Level Attribute Addition
>>> class Employee:
... def __init__(self, full_name=None, years_old=None):
... self.full_name = full_name
... self.years_old = years_old
...
>>> worker = Employee("Alice", 28)
Even without a department attribute defined, you can dynamically attach it:
>>> worker.department = "Engineering"
>>> worker.department
'Engineering'
This demonstrates Python's capability for dynamic instance attribute injecsion.
Class-Level Attribute Propagation
New instances won't inherit dynamically added instance attributes:
>>> worker2 = Employee("Bob", 35)
>>> worker2.department
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Employee' object has no attribute 'department'
To share attributes across all instances, bind them to the class:
>>> Employee.department = "Unassigned"
>>> worker3 = Employee("Charlie", 30)
>>> print(worker3.department)
Unassigned
Runtime Method Injection
>>> class Developer:
... def __init__(self, name=None, level=None):
... self.name = name
... self.level = level
...
... def code(self):
... print(f"{self.name} is writing code")
...
>>> def debug(self, bug_count):
... print(f"{self.name} fixed {bug_count} bugs today")
...
>>> dev = Developer("Diana", "Senior")
>>> dev.code()
Diana is writing code
>>> dev.debug()
Traceback (most recent call last):
AttributeError: 'Developer' object has no attribute 'debug'
Attach methods using types.MethodType:
>>> import types
>>> dev.debug = types.MethodType(debug, dev)
>>> dev.debug(5)
Diana fixed 5 bugs today
Complete Method Binding Example
import types
class Developer:
team_size = 0
def __init__(self, name=None, level=None):
self.name = name
self.level = level
def code(self):
print(f"{self.name} is writing code")
def debug(self, bug_count):
print(f"{self.name} fixed {bug_count} bugs today")
@classmethod
def update_team_size(cls, size):
cls.team_size = size
@staticmethod
def get_framework():
return "Django"
# Create instance and attach instance method
engineer = Developer("Eve", "Lead")
engineer.debug = types.MethodType(debug, engineer)
engineer.debug(3)
# Bind class method
Developer.update_team_size = update_team_size
Developer.update_team_size(10)
print(Developer.team_size)
# Bind static method
Developer.get_framework = get_framework
print(Developer.get_framework())
Output:
Eve fixed 3 bugs today
10
Django
Removing Attributes and Methods
Use del or delattr():
del engineer.level
delattr(engineer, 'name')
Dynamic languages offer flexibility but require careful management to avoid unexpected behavior.
Restricting Dynamic Behavior with __slots__
To limit permissible attributes, define __slots__:
>>> class Account:
... __slots__ = ('id', 'balance')
...
>>> acc = Account()
>>> acc.id = 1001
>>> acc.balance = 5000.00
>>> acc.owner = "John"
Traceback (most recent call last):
AttributeError: 'Account' object has no attribute 'owner'
Important: __slots__ applies only to the defining class, not subclasses:
>>> class PremiumAccount(Account):
... pass
...
>>> premium = PremiumAccount()
>>> premium.owner = "Jane" # Works in subclass
Naming Conventions and Encapsulation
name: Public attribute_status: Single underscore indicates internal use (not imported withfrom module import *)__password: Double underscore triggers name mangling for privacy__magic__: Double underscores denote Python magic methodsclass_: Trailing underscore avoids keyword conflicts
Name Mangling Example
class User:
def __init__(self, username, _status, __password):
self.username = username
self._status = _status
self.__password = __password
def display(self):
print(self.username, self._status, self.__password)
def _internal_check(self):
print("Internal verification")
def __private_method(self):
print("Sensitive operation")
class Admin(User):
def __init__(self, username, _status, __password):
self.username = username
self._status = _status
self.__password = __password # Creates new attribute, doesn't override parent
Property Decorators
Traditional Getter/Setter Pattern
class BankAccount:
def __init__(self):
self.__funds = 0
def get_funds(self):
return self.__funds
def set_funds(self, amount):
if isinstance(amount, int) and amount >= 0:
self.__funds = amount
else:
raise ValueError("Amount must be positive integer")
Using property()
class BankAccount:
def __init__(self):
self.__funds = 0
def get_funds(self):
return self.__funds
def set_funds(self, amount):
if isinstance(amount, int) and amount >= 0:
self.__funds = amount
else:
raise ValueError("Amount must be positive integer")
funds = property(get_funds, set_funds)
account = BankAccount()
account.funds = 1000
print(account.funds)
Modern @property Syntax
class BankAccount:
def __init__(self):
self.__funds = 0
@property
def funds(self):
return self.__funds
@funds.setter
def funds(self, amount):
if isinstance(amount, int) and amount >= 0:
self.__funds = amount
else:
raise ValueError("Amount must be positive integer")
account = BankAccount()
account.funds = 5000
print(account.funds)
Metaclass Programming
Classes as First-Class Objects
>>> class WidgetFactory:
... pass
...
>>> factory = WidgetFactory()
>>> print(factory)
<__main__.WidgetFactory object at 0x...>
The class itself is an object created at definition time. You can:
- Assign it to variables
- Copy it
- Add attributes dynamically
- Pass it as function arguments
>>> WidgetFactory.version = "1.0"
>>> print(WidgetFactory.version)
1.0
>>> def analyze(cls):
... print(f"Analyzing {cls}")
...
>>> analyze(WidgetFactory)
Analyzing <class '__main__.WidgetFactory'>
Dynamic Class Creation with type()
type() can create classes programmatically:
# Traditional definition
class Device:
pass
# Dynamic creation
Gadget = type('Gadget', (), {})
type(name, bases, namespace) parameters:
name: Class name stringbases: Tuple of base classesnamespace: Dictionary of attributes and methods
Creating Classes with Attributes
>>> Product = type('Product', (), {'category': 'Electronics', 'price': 99.99})
>>> item = Product()
>>> print(item.category)
Electronics
Creating Classes with Methods
def calculate_discount(self, percent):
return self.price * (1 - percent / 100)
@staticmethod
def warranty():
return "2 years"
@classmethod
def update_category(cls, new_cat):
cls.default_category = new_cat
ShoppingItem = type('ShoppingItem', (), {
'price': 199.99,
'apply_discount': calculate_discount,
'get_warranty': warranty,
'set_category': update_category
})
cart = ShoppingItem()
print(cart.apply_discount(15))
print(ShoppingItem.get_warranty())
What Are Metaclasses?
Metaclasses create classes. Since class are objects, metaclasses are their constructors:
MyClass = MetaClass() # Metaclass creates class
instance = MyClass() # Class creates instance
type is Python's built-in metaclass. Every object's __class__.__class__ points to type:
>>> number = 42
>>> number.__class__.__class__
<type 'type'>
>>> def func(): pass
>>> func.__class__.__class__
<type 'type'>
Custom Metaclass Creation
Define a metaclass by inheriting from type:
class ValidatorMeta(type):
def __new__(meta, name, bases, namespace):
# Prefix non-magic attributes with 'validated_'
filtered = {}
for key, value in namespace.items():
if key.startswith('__'):
filtered[key] = value
else:
filtered[f'validated_{key}'] = value
return super().__new__(meta, name, bases, filtered)
# Python 3 syntax
class DataModel(metaclass=ValidatorMeta):
field1 = "value1"
field2 = 42
print(DataModel.validated_field1) # value1
print(hasattr(DataModel, 'field1')) # False
When to Use Metaclasses?
As Tim Peters noted: "Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don't."
Use cases include:
- API frameworks requiring automatic registration
- Enforcing coding conventions across class hierarchies
- Dynamic interface generation
- Advanced ORM implementations
The core mechanism remains: intercept class creation, modify attributes, return the transformed class.