|
118 | 118 | |
119 | 119 | obj = self.__new__(self, *args, **kwargs) |
120 | 120 | |
121 | | - if obj is not None and isinstance(obj, self) and hasattr(obj, '__init__'): |
122 | | - obj.__init__(*args, **kwargs) |
| 121 | + obj.__init__(*args, **kwargs) |
123 | 122 | |
124 | 123 | return obj |
125 | 124 |
|
|
169 | 168 | y: p_arg_2 |
170 | 169 | z: k_arg |
171 | 170 |
|
| 171 | +بر اساس نمونه کد بالا، میتوان فرآیند ایجاد یک شی در پایتون را به ترتیب زیر شرح داد: |
172 | 172 |
|
| 173 | +* کلاس Sample فراخوانی میشود (سطر ۳۹)، در نتیجه به صورت خودکار متد ``__call__`` کلاسِ کلاس Sample یا همان متا کلاس آن (MetaClass) فراخوانی میشود. توجه داشته باشید که مقدار پارامتر ``self`` در این متد برابر با کلاس Sample میباشد (به خروجی سطر پنجم توجه شود - ``<'class '__main__.Sample>``)، چرا که شی فراخوانی کننده این متد اکنون خود کلاس Sample میباشد. |
173 | 174 |
|
| 175 | +* داخل متد ``__call__`` از متا کلاس، ابتدا متد ``__new__`` تعریف شده داخل کلاس Sample فراخوانی میشود (سطر ۹). توجه داشته باشید که متد ``__new__`` از کلاس Sample نیز متد مرتبط از کلاس ``object`` (به عنوان superclass) فراخوانی شده است (سطر ۲۴). حاصل یک شی جدید از کلاس Sample خواهد بود که به داخل متد ``__call__`` از متاکلاس برگردانده میشود. |
174 | 176 |
|
| 177 | +* داخل متد ``__call__`` از متا کلاس، اینبار متد ``__init__`` فراخوانی میگردد - پیش از برگرداندن شی Sample ایجاد شده (سطر ۱۱). این متد عملیات initialize یا مقداردهی اولیه را بر روی شی تازه ایجاد شده از کلاس Sample به انجام میرساند. |
175 | 178 |
|
| 179 | +* در انتها متد ``__call__`` از متا کلاس، شی Sample را بازمیگرداند (سطر ۱۳). |
| 180 | + |
| 181 | +* توجه داشته باشید از آنجا که احتمال ارسال آرگومان به دو صورت «positional arguments» و «keyword arguments» وجود دارد، پارامترها و آرگومانها به گونهای تعریف و ارسال گردیدند که هر دو حالت پوشش داده شود: ``args, **kwargs*`` |
| 182 | + |
| 183 | +با استفاده از مثال پیش، یک کاربرد جالب و مهم از قابلیت تعریف متا کلاس در پایتون را بررسی کردیم، مثالی که شما را با روند ایجاد شی نیز بیشتر آشنا کرد. |
| 184 | + |
| 185 | +در انتهای این بخش جا دارد با امکان تعریف یک متد از نوع Class Method و نام ``__prepare__`` [`اسناد پایتون <https://docs.python.org/3/reference/datamodel.html#preparing-the-class-namespace>`__] در پایتون آشنا شویم. به صورت پیشفرض مفسر پایتون پس از اینکه متا کلاسِ یک کلاس را تشخیص میدهد، بلافاصله به دنبال ``__prepare__`` در آن میگردد و چنانچه پیادهسازی شده باشد، آن را فراخوانی و آرگومانهای «متا کلاس»، «نام کلاسی که قرار است یک شی از آن ایجاد گردد»، «یک شی تاپل حاوی فهرست superclassهای آن کلاس - با حفظ ترتیب» و «تعدادی keyword argumentهای احتمالی (آرگومانهای نام=مقدار)» را به آن ارسال میکند [`PEP 3115 <https://www.python.org/dev/peps/pep-3115/#invoking-the-metaclass>`__]. خروجی این متد میبایست یک شی دیکشنری (``dict``) باشد که در زمان ایجاد و ارزیابی کلاس، مورد استفاده قرار میگیرد. این متد قبل از ``__call__`` فراخوانی میشود و ما میتوانیم از آن برای قرار دادن مقادیری برای استفاده در کلاسهایی که توسط متا کلاس ایجاد میگردند قرار دهیم. برای درک بهتر کاربرد این متد، به نمونه کد زیر توجه نمایید: |
| 186 | + |
| 187 | +.. code-block:: python |
| 188 | + :linenos: |
| 189 | +
|
| 190 | + class MetaClass(type): |
| 191 | + |
| 192 | + @classmethod |
| 193 | + def __prepare__(metacls, name, bases, **kwargs): |
| 194 | + print('\n------->>> MetaClass __prepare__') |
| 195 | + print('metaclass: ', metacls) |
| 196 | + print('name: ', name) |
| 197 | + print('superclasses: ', bases) |
| 198 | + print('extra arguments: ', kwargs) |
| 199 | +
|
| 200 | + return {"class_code": 1633} |
| 201 | + |
| 202 | + |
| 203 | + class Sample(metaclass=MetaClass): |
| 204 | + |
| 205 | + @classmethod |
| 206 | + def print_extra_info(cls): |
| 207 | + print('\n------->>> Sample print_extra_info') |
| 208 | +
|
| 209 | + print ('class_code:', cls.__dict__['class_code']) |
| 210 | + |
| 211 | + |
| 212 | + print ('Sample.__dict__:\n', Sample.__dict__) |
| 213 | + Sample.print_extra_info() |
| 214 | +
|
| 215 | +:: |
| 216 | + |
| 217 | + ------->>> MetaClass __prepare__ |
| 218 | + metaclass: <class '__main__.MetaClass'> |
| 219 | + name: Sample |
| 220 | + superclasses: () |
| 221 | + extra arguments: {} |
| 222 | + Sample.__dict__: |
| 223 | + {'class_code': 1633, '__module__': '__main__', 'print_extra_info': <classmethod object at 0x7f090a6aa5c0>, '__dict__': <attribute '__dict__' of 'Sample' objects>, '__weakref__': <attribute '__weakref__' of 'Sample' objects>, '__doc__': None} |
| 224 | + |
| 225 | + ------->>> Sample print_extra_info |
| 226 | + class_code: 1633 |
| 227 | + |
| 228 | +دانستن ترتیب مراحل فراخوانی متدهای معرفی شده و همچنین قابلیت پیادهسازی و شخصیسازی آنها میتواند در شرایط خاص خودش برای برنامهنویس بسیار کارگشا باشد. |
176 | 229 |
|
177 | 230 |
|
178 | 231 |
|
179 | 232 |
|
180 | | -بر اساس تعریف ارائه شده در [`اسناد پایتون <https://docs.python.org/3/glossary.html#term-metaclass>`__]، تعریف کلاس (دستور ``class``) باعث ایجاد «نام کلاس»، «یک شی دیکشنری از کلاس» و «یک شی لیست حاوی superclassهای آن کلاس» میشود، متاکلاس این سه آرگومان را دریافت و شی کلاس را ایجاد میکند. |
181 | 233 |
|
182 | 234 |
|
183 | 235 |
|
|
0 commit comments