Python Meta Classes (original) (raw)
A metaclass is a class that defines how other classes are created and behave. Just like objects are created from classes, classes themselves are created from metaclasses. In short:
- Class: creates objects
- Metaclass: creates classes
By default, Python uses the built-in metaclass "type" to create all classes.
Steps to Create a Metaclass
- **Define a metaclass: Create a class that inherits from type. Optionally, define __new__ or __init__ to customize class creation.
- **Use the metaclass in a class: Specify metaclass=YourMeta when defining a class.
- **Class is created by the metaclass: The metaclass can modify attributes or methods of the class automatically.
- **Create instances of the class: Instances work normally, the metaclass only affects the class itself. Python `
class Meta(type): def new(cls, name, bases, dct): dct['greet'] = lambda self: f"Hello from {name}!" return super().new(cls, name, bases, dct)
class Person(metaclass=Meta): def init(self, name): self.name = name
p = Person("Olivia")
print(p.greet())
`
**Explanation:
- **Meta(type): Defines a metaclass that can modify classes when they are created.
- **__new__: Adds a greet method automatically to any class using this metaclass.
- **Person(metaclass=Meta): Class created using the metaclass, so it gets the greet method.
- **p = Person("Olivia"): Creates an instance of Person.
- **p.greet(): Calls the method injected by the metaclass.
Creating Subclass
Subclasses can also be created dynamically using type.
Python `
def init(self, ftype): self.ftype = ftype
def getFtype(self): return self.ftype
FoodType = type('FoodType', (object,), { 'init': init, 'getFtype': getFtype })
def vegFoods(self): return {'Spinach', 'Bitter Guard'}
VegType = type('VegType', (FoodType,), { 'vegFoods': vegFoods })
v = VegType("Vegetarian") print(v.getFtype()) print(v.vegFoods())
`
Output
Vegetarian {'Bitter Guard', 'Spinach'}
**Explanation:
- VegType inherits from FoodType dynamically.
- vegFoods method is added via the attributes dictionary.
Metaclasses can be inherited just like normal classes. When a class inherits from a metaclass, it becomes an instance of that metaclass.
Python `
class MetaCls(type): pass
A = MetaCls('A', (object,), {}) class B(object): pass class C(A, B): pass
print(type(A))
print(type(B))
print(type(C))
`
Output
<class '__main__.MetaCls'> <class 'type'> <class '__main__.MetaCls'>
**Explanation:
- A is created by MetaCls: type is MetaCls
- B is normal: type is type
- C inherits from A: type is MetaCls
A class cannot inherit from two different metaclasses. Python will raise a TypeError.
Python `
class MetaCls(type): pass
Create class A using MetaCls
A = MetaCls('A', (object,), {}) print("Type of A:", type(A))
class DiffMetaCls(type): pass
Create class B using DiffMetaCls
B = DiffMetaCls('B', (object,), {}) print("Type of B:", type(B))
class C(A, B): pass
print("Type of C:", type(C))
`
Error:
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
**Explanation:
- Python allows only one metaclass per class.
- 'C' inherits from two different metaclasses → conflict occurs.
Metaclasses are useful when you want to control class creation or enforce rules.
**1. Class Verification: This example shows how a metaclass can enforce rules on class attributes.
Python `
class MainClass(type): def new(cls, name, bases, attrs): if 'foo' in attrs and 'bar' in attrs: raise TypeError(f"Class {name} cannot have both foo and bar") return super().new(cls, name, bases, attrs)
This will raise an error
class SubClass(metaclass=MainClass): foo = 42 bar = 34
`
**Explanation:
- Ensures a class cannot have both foo and bar attributes.
- Useful for interface enforcement.
**2. Prevent Subclass Inheritance: This example shows how a metaclass can treat abstract classes differently, preventing certain checks for them while enforcing rules on normal classes.
Python `
class MetaCls(type): def new(cls, name, bases, attrs): if attrs.pop('abstract', False): print('Abstract Class:', name) return super().new(cls, name, bases, attrs) print('Normal Class:', name) return super().new(cls, name, bases, attrs)
class AbsCls(metaclass=MetaCls): abstract = True
class NormCls(metaclass=MetaCls): foo = 42
`
Output
Abstract Class: AbsCls Normal Class: NormCls
**Explanation:
- Abstract classes can skip metaclass checks.
- Normal classes are validated by the metaclass.
Dynamic Generation of Classes
Dynamic class generation allows you to create classes at runtime instead of defining them statically in code.
Python `
class FoodType: events = []
def __init__(self, ftype, items):
self.ftype = ftype
self.items = items
FoodType.events.append(self)
def run(self):
print("Food Type:", self.ftype)
print("Food Menu:", self.items)
@staticmethod
def run_events():
for e in FoodType.events:
e.run()def sub_food(ftype): class_name = ftype.capitalize() def init(self, items): FoodType.init(self, ftype, items) globals()[class_name] = type(class_name, (FoodType,), {'init': init})
Create dynamic classes
[ sub_food(ftype) for ftype in ["Vegetarian", "Nonvegetarian"] ]
Vegetarian(['Spinach', 'Bitter Guard']) Nonvegetarian(['Meat', 'Fish']) FoodType.run_events()
`