السلام عليكم
وجدت اليوم ميزة مخفية في دوال بايثون، انظر لطريقة تعريف دالة في بايثون:
funcdef ::= [decorators] "def" funcname "(" [parameter_list] ")" ["->" expression] ":" suite
...
parameter ::= identifier [":" expression]الأول يشيران لأنه يمكنك إضافة سهم -> بعدما تقفل أقواس الدالة () يحتوي تعبير، expression مثل:
def foo() -> 1 + 2: pass
والثاني يعني أنه يمكنك أيضاً إضافة نقطتين بعد أسماء معاملات الدالة:يتبعها تعبير مثل:
def foo(a : print('a'), b : 'ABC', d : 123): passقصة هذه التعابير مشروحة في هذه الفقرة:
اقتباس
Parameters may have annotations of the form “: expression” following the parameter name. Any parameter may have an annotation even those of the form *identifier or **identifier. Functions may have “return” annotation of the form “-> expression” after the parameter list. These annotations can be any valid Python expression and are evaluated when the function definition is executed. Annotations may be evaluated in a different order than they appear in the source code. The presence of annotations does not change the semantics of a function. The annotation values are available as values of a dictionary keyed by the parameters’ names in the __annotations__ attribute of the function object.
المختصر أن تلك التعابير ستجدها في خاصية اسمها __annotations__ في الدالة، انظر هنا:
>>> def foo(a : 'argument expr') -> 'arrow expr':
... pass
...
>>>
>>> foo.__annotations__
{'a': 'argument expr', 'return': 'arrow expr'}
>>>عبارة عن قاموس لاحظ أن المفتاح return يشير لما بعد السهم، هذا ماتقوم به. في ايش ممكن نستخدمها؟ :thinking:
عملت تطبيق بسيط بمساعدها ومساعدة الـdecorators (تحتاج لها مقالة أخرى) لعمل طريقة بسيطة وأنيقة للتحقق من نوع المعاملات المررة للدالة والقيمة المعادة:
def typecheck(func):
def wrapper(*args, **kwargs):
for idx, arg in enumerate(func.__code__.co_varnames):
if arg in func.__annotations__:
if not isinstance(args[idx], func.__annotations__[arg]):
raise TypeError('Invalid argument type ({})'.format(arg))
r = func(*args, **kwargs)
if 'return' in func.__annotations__:
return func.__annotations__['return'](r)
return r
return wrapperطريقة استخدام هذا الـdecorators بسيطة، ضع @ ثم typecheck فوق اسم الدالة، الآن يمكنك تحديد نوع المعاملات عن طريق وضع : متبوعة بنوع المعامل، وإذا أدرت تحديد نوع القيمة المعادة، ضع بعد السهم -> نوع الكائن المعاد.
بعض الأمثلة:
>>> @typecheck ... def foo(a:int, b:str): ... print((a, b)) ... >>> >>> foo(1, 'ABCD') (1, 'ABCD') >>> >>> foo(1, 3333) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 6, in wrapper TypeError: Invalid argument type (b) >>>
لاحظ أنه حصل خطأ لأن المعامل الثاني ليس فئة من str، بعض الأمثلة:
>>> @typecheck ... def foo(a, b:float) -> str: ... return a + b ... >>> >>> foo(2, 3.14) '5.140000000000001' >>>
لاحظ أن a يقبل أي نوع، وأن القيمة المعادة صارت str.
بالتوفيق :lol:
للإستزادة: