ما هي Metaclasses في python؟

زياد العتيبيمنذ 3 سنوات

اولا ما هي Metaprogramming؟

تخيل أنك تصنع سيارة بنفسك، وصنعت عدة سيارات. ثم فكرت أنك يمكنك تطوير نفسك وأقمت مصنع لصنع السيارات، وبعدها فكرت بأنك يمكنك تطوير نفسك أكثر وجعل مشروعك أفضل وهو أن تقيم مصنع يصنع مصانع تصنع السيارات!

هذه هي الMetaprogramming هي انك تصنع برنامج(كود) قادر على كتابة او تغيير برامج اخرى.

python تدعم Metaprogramming للclasses  باسم Metaclasses.

مبرمجو python يستخدموا Metaclasses سواء كانوا يعرفوا ذلك ام لا. عندما تحتاج لعمل metaclass سوف تقوم بإنشاء custom metaclass، وهي لا تستعمل كثيرًا لوجود حلول أبسط ولكن فهمها سيمكنك من معرفة python classes بشكل أفضل.

 

>>> class Foo:
...     pass
>>> obj = Foo()
>>> obj.__class__
<class '__main__.Foo'>
>>> type(obj)
<class '__main__.Foo'>
>>> obj.__class__ is type(obj)
True

في python تستطيع تحديد نوع اي object بطريقتين  الاولى بtype(obj) والثانية ب obj.__class__ كما هو ظاهر في الكود

تذكر ان كل شيء في python هو object. الclass هو object ايضا. اذا ما نوع الclass؟

 

>>> class Foo:
...     pass
...
>>> x = Foo()

>>> type(x)
<class '__main__.Foo'>

>>> type(Foo)
<class 'type'>

نوع x هو Foo لكن نوع Foo هو type. لماذا؟ بشكل عام نوع اي Class في python هو type.

أيضا نوع built-in classes التي نعرفها هو type

>>> for t in int, float, dict, list, tuple:
...     print(type(t))
...
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>

 

للمعلومية نوع type هو type ايضا!

>>> type(type)
<class 'type'>

 

طيب ما هو type؟ هو metaclass كل الclasses في python هي instances منه. مثل ما اي object هو instance من class. في المثال بالأعلى

  • x هو instance من Foo class
  • Foo هو instance من type metaclass
  • type هو instance من type metaclass ايضا يعني هو instance من نفسه

s

custom metaclass

انظر للمثال هذا

>>> class Foo:
...     pass
...
>>> f = Foo()

العبارة Foo() تعمل instance جديد من Foo class. لما يقرا interpreter هذا السطر فهو يعمل عدة أشياء

  • نعمل call ال __call__() من Foo's parent class الذي هو type metaclass كما قلنا. اذا يتم عمل call ل__call__() الخاصة بtype.
  • عندما يتم عمل call ل__call__() يتم عمل invoked لاثنين من  methods هم:
    • __new__()
    • __init__()

اذا لم يتم انشاء __new__() و__init__() في Foo فإن يتم وراثة هذه الmethods من type metaclass. لكن اذا تم عملها هنا يحدث override وانت تغيرها بحسب ما تريد عندما يتم عمل instance من Foo class.

>>> def new(cls):
...     x = object.__new__(cls)
...     x.attr = 100
...     return x
...
>>> Foo.__new__ = new

>>> f = Foo()
>>> f.attr
100

>>> g = Foo()
>>> g.attr
100

هنا مع كل مرة تعمل instance من Foo class يأتي معه متغير attr. لان تم تغيير __new__ . 

طيب الclass ايضا object اذا نريد عمل نفس الشيء في كل مرة نعمل فيها class جديد ياتي معه متغير attr

ممكن نعمل شيء مثل هذا

>>> def new(cls):
...     x = type.__new__(cls)
...     x.attr = 100
...     return x
...
>>> type.__new__ = new
Traceback (most recent call last):
  File "<pyshell#77>", line 1, in <module>
    type.__new__ = new
TypeError: can't set attributes of built-in/extension type 'type'

حدث خطأ ما!. لماذا؟ لان python لا تسمح بتعديل type metaclass. طيب هل هناك طريقة اخرى؟

نعم تستطيع عمل metaclass الخاص بك الذي يرث من type 

 

>>> class Meta(type):
...     def __new__(cls, name, bases, dct):
...         x = super().__new__(cls, name, bases, dct)
...         x.attr = 100
...         return x
...

في definition header عملت class باسم Meta يرث من type الذي هو metaclass مما يجعل Meta هو metaclass ايضا.

الان عمل override ل__new__ وهي تعمل الآتي:

  • في السطر الاول تعمل call ل __new__() للparent metaclass وهو type  عبر super() ليعمل class جديد
  • اسناد متغير attr للclass مع قيمة 100
  • يعمل return للclass الجديد 

الان بقي نعمل class باسم Foo ونحدد أن الmetaclass هو الذي عملناه بدلا من type metaclass ونعمل الطريقة هذه باستخدام metaclass keyword

>>> class Foo(metaclass=Meta):
...     pass
...
>>> Foo.attr
100

وهكذا Foo اخذ المتغير attr ونستطيع عمله طبعا مع اكثر من class

>>> class Bar(metaclass=Meta):
...     pass
...
>>> class Qux(metaclass=Meta):
...     pass
...
>>> Bar.attr, Qux.attr
(100, 100)

هل حقا نحتاج استخدامه؟

في python هناك احيانا طرق ابسط تقوم بنفس المهمة لكن فهم الmetaclass يجعلنا نفهم python classes بشكل افضل

من الحلول الابسط:

وراثة inheritance:

>>> class Base:
...     attr = 100
...

>>> class X(Base):
...     pass
...

>>> class Y(Base):
...     pass
...

>>> class Z(Base):
...     pass
...

>>> X.attr
100
>>> Y.attr
100
>>> Z.attr
100

Class Decorator:

>>> def decorator(cls):
...     class NewClass(cls):
...         attr = 100
...     return NewClass
...
>>> @decorator
... class X:
...     pass
...
>>> @decorator
... class Y:
...     pass
...
>>> @decorator
... class Z:
...     pass
...

>>> X.attr
100
>>> Y.attr
100
>>> Z.attr
100

مراجع للاستزادة:

https://www.python.org/dev/peps/pep-3115/

https://docs.python.org/3/reference/datamodel.html#metaclasses

 

كلمات دليلية: advanced python
1
إعجاب
1818
مشاهدات
0
مشاركة
1
متابع

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !