كيف يعمل هذا الكود ؟ decorator


#1

:thinking: :thinking:
Capture


#2

الأمر بسيط جداً صديقي عثمان.’

هذا اسمه decorator design pattern, يعني نمط التنميق بالعربي :smile:

نفترض أن عندك اثنين دوال function مثلاً:

def say_hello():
    print('hello')

def say_hi():
    print('hi')

وأنت تريد طباعة جملة ثانية داخل هذه الدالتين:

def say_hello():
    print('Printing........')
    print('hello')

def say_hi():
    print('Printing........')
    print('hi')

لكن هذا يخرق اثنين مبادئ principles:

  1. مبدأ DRY - Dont Repeat Yourself الذي يقول:

التكرار سيء… استعملنا نفس السطر print('printing') مرتين, ولازمنا نعدل عليه مرتين في المستقبل لو أردنا التعديل عليه.

  1. مبدأ Open-Closed الذي يقول:

الكود يجب أن مفتوح للإضافات Open… لكن مغلق للتعديلات Closed

يعني بدون ما تعدل الدالة أنت تريد تضيف عليها شيء.

لكن كيف ؟

هنا يأتي decorator يحل لنا المشكلة

تكتب فقط:

def printer(my_func):
    def wrapper():
        print('Printing........')
        my_func()
    return wrapper

فكرته أنه تدخل عليه دالة مكان my_func, وهو يقوم بتشغيلها, لكن قبلها يقوم بالذي نريد.

يعني أول شيء print('Printing........') وبعدين يستدعي الدالة التي تدخل عليه my_func

بهذا الشكل:

def printer(my_func):
    def wrapper():
        print('Printing........')
        my_func()
    return wrapper


def say_hello():
    print('hello')

say_hello = printer(say_hello)
say_hello()

هكذا هو يمرر الدالة say_hello على الدالة printer, ثم يستدعيها.

شكل الكود قبيح نوعاً ما… صح ؟

لدى بايثون طريقة أجمل بهذا الشكل:

@printer
def say_hello():
    print('hello')

say_hello()

النتيجة النهائية:

def printer(my_func):
    def wrapper():
        print('Printing........')
        my_func()
    return wrapper


@printer
def say_hello():
    print('hello')


@printer
def say_hi():
    print('hi')


say_hello()
say_hi()

لاحظ الآن بدون خرق Open-Closed principle و Dont Repeat Yourself أصبح بإمكاننا إضافة أكواد على الدوال بدون التعديل عليها.