Skip to content

Commit c65c396

Browse files
author
Saeid Darvish
committed
l21: complete Decorator
1 parent 5ca3abd commit c65c396

1 file changed

Lines changed: 119 additions & 0 deletions

File tree

lessons/l21.rst

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,130 @@ Decorators
344344
قراردادن Decorator بر روی کلاس
345345
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
346346

347+
در زبان برنامه‌نویسی پایتون می‌توان یک Decorator را به کل یک کلاس اعمال کرد، در این صورت نیز تفاوتی با آنچه در توابع دیدم، نمی‌کند. تنها در این حالت، این کلاس است که به Decorator ارسال می‌گردد. دو نمونه کد زیر معادل یکدیگر هستند::
348+
349+
350+
def decorator_name(a_class):
351+
def wrapper():
352+
# Do Something!
353+
print('Class name:', a_class.__name__)
354+
return a_class()
355+
356+
return wrapper
357+
358+
359+
::
360+
361+
# 1
362+
363+
@decorator_name
364+
class Sample():
365+
pass
366+
367+
368+
sample = Sample()
369+
370+
371+
::
372+
373+
# 2
374+
375+
class Sample():
376+
pass
377+
378+
SampleWrapper = decorator_name(Sample)
379+
sample = SampleWrapper()
380+
381+
382+
::
383+
384+
# Output
385+
386+
Class name: Sample
387+
347388

348389

349390
کلاس به عنوان Decorator
350391
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
351392

393+
در زبان برنامه‌نویسی پایتون می‌توان از کلاس‌ها همچون توابع برای ایجاد Decorator استفاده کرد. در این صورت شی‌ای که Decorator به آن اعمال شده است از طریق متد ``__init__`` دریافت می‌گردد. همچنین می‌بایست متد ``__call__`` را پیاده‌سازی کرده باشیم تا اشیای کلاس قابلیت callable را داشته باشند (درس هفدهم)، عملیات اصلی Decorator می‌بایست داخل این متد پیاده‌سازی گردد:
394+
395+
396+
397+
::
398+
399+
class CountCalls:
400+
def __init__(self, func):
401+
self.func = func
402+
self.num_calls = 0
403+
404+
def __call__(self):
405+
self.num_calls += 1
406+
print(f"Call {self.num_calls} of {self.func.__name__!r}")
407+
return self.func()
408+
409+
::
410+
411+
# 1
412+
413+
@CountCalls
414+
def func():
415+
''' a function'''
416+
417+
print(func.__doc__)
418+
func()
419+
func()
420+
421+
422+
::
423+
424+
# 2
425+
426+
def func():
427+
''' a function'''
428+
429+
obj = CountCalls(func)
430+
431+
print(obj.__doc__)
432+
obj()
433+
obj()
434+
435+
436+
::
437+
438+
439+
# Output
440+
441+
None
442+
Call 1 of 'func'
443+
Call 2 of 'func'
444+
445+
446+
447+
**functools.update_wrapper**
448+
449+
همانند کاربرد تابع ``wraps`` از ماژول ``functools`` در هنگام ساخت Decorator از توابع، در اینجا نیز می‌توانیم جهت حفظ اطلاعات مربوط به تابع اصلی، این‌بار از تابع ``update_wrapper`` این ماژول استقاده کنیم [`اسناد پایتون <https://docs.python.org/3/library/functools.html#functools.update_wrapper>`__] - اگر کلاس CountCalls را به صورت زیر تغییر دهیم، آنگاه خروجی هر دو حالت نیز به شرح زیر تغییر خواهد کرد، چرا که اکنون ``__doc__`` در دسترس باقی مانده است::
450+
451+
452+
import functools
453+
454+
class CountCalls:
455+
def __init__(self, func):
456+
functools.update_wrapper(self, func)
457+
self.func = func
458+
self.num_calls = 0
459+
460+
def __call__(self):
461+
self.num_calls += 1
462+
print(f"Call {self.num_calls} of {self.func.__name__!r}")
463+
return self.func()
464+
465+
466+
::
467+
468+
a function
469+
Call 1 of 'func'
470+
Call 2 of 'func'
352471

353472

354473
Descriptors

0 commit comments

Comments
 (0)