متا کلاس در پایتون + ارثبری چندگانه در پایتون؛ ۲ مفهوم مهم

اگر با کلاسها، اشیا، ارثبری و super() در پایتون آشنا شدهاید، احتمالاً در پروژههای پیشرفتهتر با دو مفهوم روبهرو میشوید که در نگاه اول کمی پیچیده به نظر میرسند: متا کلاس (Metaclass) و ارثبری چندگانه (Multiple Inheritance).
این دو مفهوم به هم مرتبطاند، اما یکسان نیستند. ارثبری چندگانه درباره این است که یک کلاس از چند کلاس والد ویژگی و رفتار دریافت کند؛ متا کلاس یک سطح بالاتر قرار دارد و مشخص میکند خودِ کلاس چگونه ساخته و کنترل شود.
در این آموزش، ابتدا هر مفهوم را جداگانه یاد میگیریم، سپس سراغ MRO، super()، مشکل Diamond و در نهایت ارتباط متا کلاس با ارثبری چندگانه میرویم. مثالها نیز بهگونهای انتخاب شدهاند که بتوانید کاربرد واقعی آنها را در کد تشخیص دهید.
متا کلاس در پایتون چیست؟
برای درک متا کلاس، ابتدا باید یک سؤال ساده را جواب دهیم:
اگر یک کلاس خودش یک شیء باشد، چه چیزی آن کلاس را میسازد؟
در پایتون تقریباً همه چیز شیء است؛ از عدد و رشته گرفته تا خود کلاسها.
مثلاً:
class User:
pass
user = User()
print(type(user))
print(type(User))
خروجی:
<class ‘__main__.User’>
<class ‘type’>
یعنی user یک نمونه از User است، اما خود User نیز یک شیء است که معمولاً نمونهای از type محسوب میشود.
به زبان ساده:
Object → ساختهشده از Class
Class → ساختهشده از Metaclass
در حالت معمول، متا کلاس پیشفرض پایتون type است. مستندات رسمی Python نیز توضیح میدهد که کلاسها بهطور پیشفرض با type() ساخته میشوند. مستندات رسمی Python درباره Metaclass
type دقیقاً چه کاری انجام میدهد؟
میتوانیم یک کلاس را به شکل سنتی بنویسیم:
class Person:
name = “Ali”
اما از نظر مفهومی، ساخت کلاس به چیزی شبیه این مربوط میشود:
Person = type(
“Person”,
(),
{“name”: “Ali”}
)
تابع type() در این حالت سه آرگومان میگیرد:
type(name, bases, namespace)
- Name: نام کلاس
- Bases: کلاسهای والد
- Namespace: ویژگیها و متدهای کلاس
بنابراین type را میتوان سازنده پیشفرض کلاسها دانست.
از دست ندین: متدهای فایل در پایتون؛ خواندن و مسیر فایل
متا کلاس سفارشی چگونه ساخته میشود؟
اگر بخواهیم فرآیند ساخت کلاس را تغییر دهیم، میتوانیم از یک متا کلاس سفارشی استفاده کنیم.
معمولاً متا کلاس از type ارثبری میکند:
class MyMeta(type):
pass
حالا میتوانیم کلاس خود را با آن بسازیم:
class User(metaclass=MyMeta):
pass
در اینجا:
User
↓
MyMeta
↓
type
یعنی User نمونهای از MyMeta است.

یک مثال کاربردی از متا کلاس
فرض کنید میخواهیم هنگام ایجاد هر کلاس، نام آن را بررسی کنیم:
class Meta(type):
def __new__(cls, name, bases, namespace):
print(“Creating:”, name)
return super().__new__(cls, name, bases, namespace)
class User(metaclass=Meta):
pass
class Product(metaclass=Meta):
pass
خروجی تقریباً چنین خواهد بود:
Creating: User
Creating: Product
نکته مهم این است که متا کلاس هنگام ساختهشدن کلاس وارد عمل میشود، نه هنگام ساخت نمونهای از آن کلاس.
این تفاوت بسیار مهم است.
user = User()
در اینجا نمونه ساخته میشود.
اما:
class User:
pass
در این مرحله خود کلاس User در حال ساختهشدن است و متا کلاس میتواند در این فرآیند دخالت کند.
فرآیند ساخت کلاس در پایتون
طبق مستندات رسمی Python، هنگام اجرای تعریف یک کلاس، مراحلی مانند تعیین MRO، انتخاب متا کلاس مناسب، آمادهسازی namespace، اجرای بدنه کلاس و در نهایت ساخت شیء کلاس انجام میشود. مستندات رسمی Data Model پایتون
به شکل ساده:
class User(…)
↓
تعیین Base Classها
↓
تعیین Metaclass
↓
آمادهسازی namespace
↓
اجرای بدنه کلاس
↓
ساخت Class Object
این موضوع نشان میدهد متا کلاس فقط یک «کلاس عجیب برای حرفهایها» نیست؛ بلکه در سطح پایینتر، بخشی از مکانیزم ساخت کلاس در پایتون است.

__new__ در متا کلاس چه کاربردی دارد؟
یکی از رایجترین جاهایی که متا کلاس سفارشی میشود، متد __new__ است:
class Meta(type):
def __new__(cls, name, bases, namespace):
print(f”Creating class: {name}”)
return super().__new__(
cls,
name,
bases,
namespace
)
سپس:
class User(metaclass=Meta):
age = 20
متا کلاس قبل از ایجاد نهایی User میتواند namespace آن را بررسی یا تغییر دهد.
مثلاً:
class Meta(type):
def __new__(cls, name, bases, namespace):
if “name” not in namespace:
raise TypeError(
f”{name} must define name”
)
return super().__new__(
cls,
name,
bases,
namespace
)
class User(metaclass=Meta):
name = “Ali”
اما:
class Product(metaclass=Meta):
pass
باعث خطا میشود.
این قابلیت میتواند برای اعتبارسنجی یا ایجاد قراردادهای خاص در یک مجموعه از کلاسها مفید باشد.
کوتاه اما کاربردی: ماژول پایتون چیست؟ ۱۲ نکته و تفاوت با تابع
__prepare__ چیست؟
یکی از بخشهای کمتر توضیحدادهشده متا کلاسها، __prepare__ است.
این متد قبل از اجرای بدنه کلاس برای آمادهکردن namespace مورد استفاده قرار میگیرد.
مثلاً:
class Meta(type):
@classmethod
def __prepare__(cls, name, bases):
print(“Preparing:”, name)
return {}
def __new__(cls, name, bases, namespace):
print(“Creating:”, name)
return super().__new__(
cls,
name,
bases,
namespace
)
این مکانیزم در PEP 3115 بهصورت رسمی توضیح داده شده است. این PEP مشخص میکند که متا کلاس میتواند قبل از اجرای بدنه کلاس namespace مورد استفاده برای تعریف اعضای کلاس را آماده کند. PEP 3115؛ مستند رسمی متا کلاسها در Python
البته در بیشتر پروژههای معمولی نیازی به پیادهسازی __prepare__ ندارید.

چه زمانی واقعاً به متا کلاس نیاز داریم؟
این یکی از مهمترین سؤالهاست.
اگر تازه متا کلاسها را یاد گرفتهاید، ممکن است تصور کنید استفاده از آنها نشانه حرفهایتر بودن کد است. اما معمولاً چنین نیست.
در بسیاری از پروژهها راهحلهایی مانند:
- تابع
- Decorator
- __init_subclass__
- Composition
- کلاس پایه معمولی
سادهتر و قابل نگهداریتر هستند.
مستندات رسمی Python برای متا کلاسها کاربردهایی مانند frameworkها، interface checking، logging، ساخت خودکار property و موارد مشابه را مطرح میکند. کاربردهای Metaclass در مستندات رسمی Python
یک قانون عملی
اگر مسئله شما این است:
«وقتی یک کلاس ساخته شد، میخواهم رفتار خاصی داشته باشد.»
ابتدا این گزینهها را بررسی کنید:
Decorator
↓
__init_subclass__
↓
Base Class
↓
Metaclass
متا کلاس معمولاً باید آخرین گزینه باشد، نه اولین گزینه.
ارثبری چندگانه در پایتون چیست؟
در ارثبری معمولی، یک کلاس معمولاً از یک کلاس پایه استفاده میکند:
class Animal:
def eat(self):
print(“Eating”)
class Dog(Animal):
pass
اما پایتون اجازه میدهد یک کلاس از چند والد ارثبری کند:
class Walker:
def walk(self):
print(“Walking”)
class Swimmer:
def swim(self):
print(“Swimming”)
class Duck(Walker, Swimmer):
pass
حالا:
duck = Duck()
duck.walk()
duck.swim()
هر دو متد در دسترس هستند.
ساختار کلاس به شکل زیر است:
Walker
\
Duck
/
Swimmer
این قابلیت Multiple Inheritance نام دارد.

مشکل اصلی ارثبری چندگانه چیست؟
مشکل زمانی ایجاد میشود که چند کلاس والد، متدی با نام یکسان داشته باشند.
class A:
def hello(self):
print(“A”)
class B:
def hello(self):
print(“B”)
class C(A, B):
pass
حالا:
c = C()
c.hello()
کدام hello() اجرا میشود؟
پاسخ با MRO مشخص میشود.
MRO در پایتون چیست؟
MRO مخفف:
Method Resolution Order
است؛ یعنی ترتیب جستوجوی پایتون برای پیدا کردن متدها و ویژگیها.
میتوانیم آن را ببینیم:
print(C.__mro__)
یا:
print(C.mro())
احتمالاً نتیجهای مشابه این خواهد بود:
C → A → B → object
پس وقتی مینویسیم:
c.hello()
پایتون ابتدا C را بررسی میکند، سپس A، بعد B و در نهایت object.
پایتون برای حل MRO در ارثبری چندگانه از الگوریتم C3 Linearization استفاده میکند. راهنمای رسمی Python درباره MRO توضیح میدهد که این ترتیب باید ویژگیهایی مانند حفظ ترتیب محلی والدها و یکنواختی سلسلهمراتب را رعایت کند. راهنمای رسمی Python درباره MRO
چرا super() در Multiple Inheritance مهم است؟
یکی از اشتباهات رایج این است که تصور کنیم:
super()
یعنی «کلاس والد من».
در واقع در ارثبری چندگانه، super() به کلاس بعدی در MRO مراجعه میکند.
مثال:
class A:
def hello(self):
print(“A”)
super().hello()
class B:
def hello(self):
print(“B”)
super().hello()
class C(A, B):
def hello(self):
print(“C”)
super().hello()
اگر C را اجرا کنیم:
C().hello()
ترتیب میتواند چنین باشد:
C
A
B
در اینجا super() در هر مرحله MRO را دنبال میکند.
این موضوع برای طراحی درست Cooperative Multiple Inheritance بسیار مهم است.

مشکل Diamond در ارثبری چندگانه
یکی از مشهورترین مشکلات Multiple Inheritance، ساختار Diamond است.
A
/ \
B C
\ /
D
کد:
class A:
def hello(self):
print(“A”)
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
ممکن است تصور کنیم A دوبار بررسی میشود، اما MRO پایتون این مسئله را مدیریت میکند.
print(D.__mro__)
ترتیبی شبیه این دریافت میکنیم:
D → B → C → A → object
این یکی از دلایل اهمیت MRO در پایتون است.
مقایسه متا کلاس و ارثبری چندگانه
| ویژگی | متا کلاس | ارثبری چندگانه |
|---|---|---|
| هدف اصلی | کنترل ساخت کلاس | ترکیب رفتار چند کلاس |
| سطح فعالیت | سطح کلاس | سلسلهمراتب کلاسها |
| ابزار مهم | type | MRO |
| مفهوم کلیدی | metaclass= | class C(A, B) |
| کاربرد رایج | Frameworkها و ساخت خودکار کلاسها | Mixins و ترکیب رفتار |
| ریسک اصلی | پیچیدگی و درگیری متا کلاسها | MRO و وابستگی پیچیده |
| جایگزینهای سادهتر | Decorator، __init_subclass__ | Composition، Mixins |
ارتباط متا کلاس و Multiple Inheritance
این قسمت از نظر عملی بسیار مهم است.
فرض کنید دو کلاس پایه داشته باشیم:
class MetaA(type):
pass
class MetaB(type):
pass
class A(metaclass=MetaA):
pass
class B(metaclass=MetaB):
pass
حالا میخواهیم:
class C(A, B):
pass
مشکل ممکن است در Metaclass Resolution ایجاد شود.
چرا؟
زیرا C باید یک متا کلاس مناسب داشته باشد که با متا کلاسهای والدهایش سازگار باشد.
مستندات رسمی Python توضیح میدهد که در زمان ساخت کلاس، پایتون باید مناسبترین متا کلاس را از متا کلاس صریح و متا کلاسهای کلاسهای پایه انتخاب کند؛ اگر هیچ متا کلاس سازگاری وجود نداشته باشد، تعریف کلاس با TypeError شکست میخورد. بخش Determining the appropriate metaclass در مستندات Python
این همان چیزی است که به آن Metaclass Conflict گفته میشود.

یک اشتباه رایج: استفاده بیدلیل از Metaclass
فرض کنید فقط میخواهید هنگام ایجاد زیرکلاس، یک پیام چاپ شود.
ممکن است کسی متا کلاس بنویسد:
class Meta(type):
def __new__(cls, name, bases, namespace):
print(name)
return super().__new__(
cls,
name,
bases,
namespace
)
اما اگر هدف فقط اجرای یک رفتار هنگام ایجاد زیرکلاس باشد، در بسیاری از موارد __init_subclass__ انتخاب سادهتری است:
class Base:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
print(“Created:”, cls.__name__)
class User(Base):
pass
بنابراین قبل از استفاده از Metaclass از خودتان بپرسید:
آیا واقعاً باید فرآیند ساخت کلاس را کنترل کنم؟
اگر جواب «نه» است، احتمالاً متا کلاس انتخاب مناسبی نیست.
تجربه عملی: برای کدام مسئله کدام ابزار را انتخاب کنیم؟
فرض کنید در یک پروژه چهار نیاز مختلف داریم.
نیاز اول: افزودن رفتار به چند کلاس
از:
Decorator
استفاده کنید.
نیاز دوم: اجرای منطق هنگام ساخت زیرکلاس
ابتدا:
__init_subclass__
را بررسی کنید.
نیاز سوم: ترکیب چند قابلیت مستقل
مثلاً:
class LoggerMixin:
…
class CacheMixin:
…
class Service(LoggerMixin, CacheMixin):
…
اینجا Multiple Inheritance میتواند منطقی باشد.
نیاز چهارم: کنترل عمیق فرآیند ساخت کلاس
در این حالت:
Metaclass
میتواند گزینه مناسبی باشد.
این تفکیک باعث میشود کد بیش از حد پیچیده نشود.

چگونه MRO را در پروژه واقعی بررسی کنیم؟
هر وقت با ارثبری چندگانه مواجه شدید، حدس نزنید.
مستقیماً MRO را ببینید:
print(MyClass.__mro__)
یا:
MyClass.mro()
مثلاً:
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
print(D.mro())
با این کار دقیقاً میبینید پایتون چه ترتیبی را برای جستوجوی متدها انتخاب کرده است.
این کار در زمان Debug کردن خطاهای super() بسیار مفید است.
چند نکته مهم برای نوشتن Multiple Inheritance سالم
اگر از ارثبری چندگانه استفاده میکنید، چند اصل را رعایت کنید:
۱. والدها را بیش از حد به هم وابسته نکنید
کلاسهایی که بهشدت به ترتیب خاصی از MRO وابستهاند، نگهداری دشوارتری دارند.
۲. در زنجیره super() همکاری کنید
اگر طراحی شما بر پایه Cooperative Multiple Inheritance است، زنجیره super() را بیدلیل قطع نکنید.
۳. MRO را بررسی کنید
این کد را فراموش نکنید:
print(MyClass.__mro__)
۴. برای ترکیب ساده رفتار، Composition را هم بررسی کنید
همیشه لازم نیست برای استفاده از چند قابلیت، چندکلاسهکردن انجام دهید.
۵. متا کلاس را فقط برای مسئلهای واقعی استفاده کنید
پیچیدهتر بودن راهحل، به معنی حرفهایتر بودن آن نیست.
جمعبندی
متا کلاس مکانیزمی برای کنترل و سفارشیسازی فرآیند ایجاد کلاسهاست. در پایتون، type متا کلاس پیشفرض محسوب میشود و میتوان با تعریف متا کلاس سفارشی، رفتار زمان ساخت کلاس را تغییر داد.
در مقابل، ارثبری چندگانه اجازه میدهد یک کلاس از چند کلاس پایه ارثبری کند. مهمترین مفاهیم مرتبط با آن MRO و super() هستند.
رابطه این دو زمانی جدیتر میشود که کلاسهای پایه متا کلاسهای متفاوتی داشته باشند؛ در چنین شرایطی ممکن است با Metaclass Conflict مواجه شوید.
برای استفاده عملی، این قانون ساده را به خاطر بسپارید:
ترکیب رفتارها
↓
Composition / Mixins
تغییر رفتار زیرکلاسها
↓
__init_subclass__
کنترل ساخت کلاس
↓
Metaclass
هدف یادگیری این مفاهیم نباید صرفاً حفظ کردن Syntax باشد. مهمتر این است که بدانید چه زمانی نباید از آنها استفاده کنید.







