class
، يستخدم التعريف الديناميكي فئة مضمنة type
.
اكتب metaclass
غالبًا ما يتم استخدام فئة النوع للحصول على نوع الكائن. على سبيل المثال مثل هذا:
h = "hello"
type(h)
<class 'str'>
لكن لها استخدامات أخرى. يمكنه تهيئة أنواع جديدة.كما تعلم ، كل شيء في بايثون هو كائن. ويترتب على ذلك أن جميع التعريفات لها أنواع ، بما في ذلك الفئات والكائنات. على سبيل المثال:
class A:
pass
type(A)
<class 'type'>
قد لا يكون من الواضح تمامًا سبب تخصيص فئة لنوع فئة
type
، على عكس حالاتها:
a = A()
type(a)
<class '__main__.A'>
a
يتم تعيين فئة كنوع
للكائن . هذه هي الطريقة التي يتعامل بها المترجم مع الكائن كمثيل للفئة. يحتوي الفصل نفسه على نوع فئة type
لأنه يرثه من الفئة الأساسية object
:
A.__bases__
(<class 'object'>,)
نوع الفصل
object
:
type(object)
<class 'type'>
يتم
object
توارث الفئة بواسطة جميع الفئات افتراضيًا ، أي:
class A(object):
pass
مثل:
class A:
pass
الفئة التي يتم تعريفها ترث الفئة الأساسية كنوع. ومع ذلك ، هذا لا يفسر سبب كون الفئة الأساسية
object
من نوع الفئة type
. النقطة type
المهمة هي أنها metaclass. كما تعلم بالفعل ، ترث جميع الفئات من الفئة الأساسية object
، وهي من النوع metaclass type
. لذلك ، تحتوي جميع الفئات أيضًا على هذا النوع ، بما في ذلك metaclass نفسه type
:
type(type)
<class 'type'>
هذه هي "نقطة نهاية الكتابة" في بايثون. يتم إغلاق سلسلة وراثة النوع في الفصل
type
. تعد metaclass type
بمثابة قاعدة لجميع الفئات في Python. من السهل التحقق من ذلك:
builtins = [list, dict, tuple]
for obj in builtins:
type(obj)
<class 'type'>
<class 'type'>
<class 'type'>
الفئة هي نوع بيانات مجردة ، ومثيلاتها لها مرجع فئة كنوع.
تهيئة أنواع جديدة بفئة النوع
عند التحقق من الأنواع ، تتم
type
تهيئة الفئة باستخدام وسيط واحد:
type(object) -> type
عند القيام بذلك ، تقوم بإرجاع نوع الكائن. ومع ذلك ، يطبق الفصل طريقة تهيئة مختلفة بثلاث وسائط ، والتي تُرجع نوعًا جديدًا:
type(name, bases, dict) -> new type
اكتب معلمات التهيئة
name
سلسلة تحدد اسم الفئة الجديدة (النوع).bases
مجموعة من الفئات الأساسية (الفئات التي سيرثها الفصل الجديد).dict
القاموس مع سمات الطبقة المستقبلية. عادة مع سلاسل في المفاتيح وأنواع قابلة للاستدعاء في القيم.
تعريف الطبقة الديناميكية
نقوم بتهيئة فئة النوع الجديد ، مع توفير جميع الحجج اللازمة ونسميها:
MyClass = type("MyClass", (object, ), dict())
MyClass
<class '__main__.MyClass'>
يمكنك العمل مع الفصل الجديد كالمعتاد:
m = MyClass()
m
<__main__.MyClass object at 0x7f8b1d69acc0>
علاوة على ذلك ، فإن الطريقة تعادل تعريف الفئة المعتاد:
class MyClass:
pass
تحديد سمات الفئة ديناميكيًا
لا فائدة من وجود فئة فارغة ، لذا فإن السؤال الذي يطرح نفسه: كيف نضيف سمات وطرق؟
للإجابة على هذا السؤال ، ضع في اعتبارك رمز التهيئة الأولي:
MyClass = type(“MyClass”, (object, ), dict())
عادةً ما يتم إضافة السمات إلى الفصل في مرحلة التهيئة كوسيطة ثالثة - القاموس. يمكنك تحديد أسماء السمات والقيم في القاموس. على سبيل المثال ، يمكن أن يكون متغيرًا:
MyClass = type(“MyClass”, (object, ), dict(foo=“bar”)
m = MyClass()
m.foo
'bar'
تعريف الطريقة الديناميكية
يمكن أيضًا تمرير الكائنات القابلة للاستدعاء إلى القاموس ، على سبيل المثال ، الطرق:
def foo(self):
return “bar”
MyClass = type(“MyClass”, (object, ), dict(foo=foo))
m = MyClass()
m.foo
'bar'
هذه الطريقة لها عيب كبير - الحاجة إلى تعريف الطريقة بشكل ثابت (أعتقد أنه في سياق مهام البرمجة الوصفية ، يمكن اعتبار هذا عيبًا). إلى جانب ذلك ، يبدو تعريف طريقة مع معلمة
self
خارج جسم الفصل غريبًا. لذلك ، دعنا نعود إلى التهيئة الديناميكية لفئة بدون سمات:
MyClass = type(“MyClass”, (object, ), dict())
بعد تهيئة فئة فارغة ، يمكنك إضافة عمليات إليها ديناميكيًا ، أي بدون تعريف ثابت واضح:
code = compile('def foo(self): print(“bar”)', "<string>", "exec")
compile
هي وظيفة مضمنة تقوم بتجميع التعليمات البرمجية المصدر في كائن. يمكن تنفيذ الكود عن طريق الوظائف exec()
أو eval()
.
تجميع معلمات الوظيفة
- مصدر
شفرة المصدر ، يمكن أن يكون رابطًا إلى وحدة نمطية. - filename
اسم الملف الذي سيتم تجميع الكائن فيه. - الوضع
إذا تم تحديده"exec"
، ستقوم الوظيفة بترجمة شفرة المصدر إلى وحدة نمطية.
نتيجة العمل
compile
كائن فئة code
:
type(code)
<class 'code'>
code
يجب تحويل
الكائن إلى طريقة. نظرًا لأن الطريقة هي وظيفة ، نبدأ بتحويل code
كائن فئة إلى كائن فئة function
. للقيام بذلك ، قم باستيراد الوحدة النمطية types
:
from types import FunctionType, MethodType
سأقوم باستيرادها
MethodType
لأنني سأحتاجها لاحقًا لتحويل الوظيفة إلى طريقة فئة.
function = FunctionType(code.co_consts[0], globals(), “foo”)
معلمات أسلوب التهيئة FunctionType
code
كائن فئةcode
.code.co_consts[0]
هو استدعاء إلى واصفco_consts
فئةcode
، وهو عبارة عن مجموعة تحتوي على ثوابت في كود الكائن. تخيل كائنًاcode
كوحدة نمطية ذات وظيفة واحدة نحاول إضافتها كطريقة للفصل.0
هو الفهرس الخاص به لأنه الثابت الوحيد في الوحدة.globals()
قاموس المتغيرات العالمية.name
معلمة اختيارية تحدد اسم الوظيفة.
والنتيجة هي وظيفة:
function
<function foo at 0x7fc79cb5ed90>
type(function)
<class 'function'>
بعد ذلك ، تحتاج إلى إضافة هذه الوظيفة كطريقة للفصل
MyClass
:
MyClass.foo = MethodType(function, MyClass)
تعبير بسيط إلى حد ما يعين وظيفتنا لطريقة الصنف
MyClass
.
m = MyClass()
m.foo()
bar
تحذير
في 99٪ من الحالات ، يمكنك الحصول على تعريفات فئة ثابتة. ومع ذلك ، فإن مفهوم metaprogramming جيد في الكشف عن العناصر الداخلية لـ Python. على الأرجح ، سيكون من الصعب عليك العثور على تطبيق الأساليب الموضحة هنا ، على الرغم من وجود مثل هذه الحالة في عملي.
هل عملت مع كائنات ديناميكية؟ ربما بلغات أخرى؟