PythonGuide/Functions Procedures
من PFWiki
محتويات |
الفصل الثالث (الدوال والأجراءات)
ال Functions واستخدامها اتسمى عليها paradigm (اتجاة برمجى ) كامل Functional Programming او Procedural Programming وكان/مازال شائع جدا للآن لغات مثل السى وباسكال بتعتمد على الFunctions كحجر اساس
ايه هى ال Function او ال Procedure ؟ هى بلوك من الأكواد اتكتب لإمكانية استخدامه اكثر من مرة
مثلا عايزين نطبع رسالة كالتالى Python rocks! لأكتر من مرة فى برنامجنا لسبب ما.. هل يعقل انى اكتب
print Python rocks!
فى كل مرة ؟ ولنفرض انى كتبتها 20 مرة هل يعقل انى اذا حبيت اعدل التعبير بدل Python rocks ل Perl rocks انى اعدل فيه 20 مرة؟ من هنا جت اهمية ال Functions وهى تستخدم ل توفير وقت ومجهود وتطبيق مبدأ DRY DRY: Don't Repeat Yourself
كل اللى عليك هو انك تحط الرسالة فى function
>>> def rocks(): print "Python rocks!"
وتستدعيها كل ماتحب
>>> rocks() Python rocks! >>> rocks() Python rocks! >>> rocks() Python rocks!
لاحظ انك اذا حبيت تعدل كلمة Python ل Perl مثلا مش هتحتاج تعدل فى اى شئ غير فى ال rocks function فقط تحولها للتالى
>>> def rocks(): print "Perl rocks!"
لاحظت الفرق ؟توفير وقت ومجهود ومش كررت نفسك فى مليون سطر
طب تمام لكن فيه مشكلة لحد الوقتى وهى اننا اضطرينا نعدل جوا ال function ونستبدل كلمة مكان كلمة وهكذا فعايزين نخليها ابسط فى الإستخدام بحيث انها تطبع اللى إحنا عايزنها تطبعه (ال دالة فيها متغير معين )فالحل هو اننا نعيد تعريفها كالتالى مثلا
>>> def rocks(thing): print thing, "rocks!"
كدا هيتم طباعة ال argument اللى هيتمرر + كلمة rocks! كالتالى
>>> rocks("Python") Python rocks! >>> rocks("Perl") Perl rocks! >>>
جميل نفس الفكرة عايزين نعمل function تجمع 2+3 وتطبع الناتج لينا
>>> def add(): print 2+3
استخدمها كالتالى
>>> add() 5
معاملات الدوال
لكن هل لاحظت شئ ؟ انها مقيدة بمعنى انى مش قادر استخدم غير 2+3 فقط طب افرض انا عايز احدد ارقام من عندى ايه الحل ؟ همممم نفس فكرة المثال اللى قبله انك تمرر الأرقام اللى تعجبك ك arguments لل add function ودا هيتم الأول 1- انك تعيد تعريف ال function كالتالى مثلا
>>> def add(first, second): print first + second
2- تستخدمها
>>> add(2, 3) 5 >>> add(3, 7) 10
ارسال ال arguments للدالة
def printArgs(first, second, third): print "First: ", first print "Second: ", second print "Third: ", third
لاحظ لإستخدام الدالة هنستدعيها كالتالى
printArgs("Hello", "Cruel", "World") #outputFirst: Hello Second: Cruel Third: World
معاملات ذات قيمة افتراضية
ولكن على فرض انى عايز احدد قيم اساسية او حتى ادخل الarguments مش بالترتيب!؟ بكل بساطة تقدر تستدعيها كدا
printArgs(third="World", second="Curel", first="Hello")
بحيث انك تحدد قيمة كل عنصر بإستخدام الإسناد (لأنهم متغيرات واضحة للدالة ;) ولتحديد قيم افتراضية ؟ مثلا عايز ادخل قيمتين او قيمة واحدة او حتى مش ادخل اى قيمة!!؟؟ تقدر تحدد دا من خلال تعريف الدالة نفسها مثلا كالتالى
def printArgs(first="Hello", second="Cruel", third="World"): print "First: ", first print "Second: ", second print "Third: ", third print "-"*20 printArgs("Bye") #Changes the first.. printArgs(second="") #only the second is set to “” printArgs()
والناتج
First: Bye Second: Cruel Third: World -------------------- First: Hello Second: Third: World -------------------- First: Hello Second: Cruel Third: World --------------------
وهكذا.. دا مفيد فى موضوع ال overloading لل function (بحيث ان يتم تنفيذ اكتر من وظيفه لنفس الfunction إعتمادا على الarguments المرسلة) تابع range
اين القيمة العائدة من الدالة من الدالة
لحد الآن ال Functions غير مفيدة لأنها مش بتعيد قيمة يعنى مش تقدر تستفيد منها فى برنامجك انك تعمل كالتالى مثلا
>>> val=add(2, 3) 5 >>> print val None
- ايه دا ؟ انا كنت متوقع ان val هتكون قيمتها 5!
دا هيتم فى حالة واحدة ان الطرف اليمين من الexpression تكون قيمته 5 لكن add(2, 3) قيمته None لأنه بيعمل print لمجموع الرقمين لكن مش بيعمل بيهم return
- ايه None ؟
None=null=nil=Nothing
جميل طب ازاى اخلى قيمة ال Function تساوى مجموع الرقمين ؟
بسيطة اعمل return بالمجموع!
كالتالى مثلا
>>> def add(first, second): return first+second >>> val=add(2, 3) >>> print val 5
هنا return عبرت عن قيمة الFunction
كتير مش عارف الفرق بين ال Functions وال Procedures على كل حال اعتبرها كالتالى ال Procedure هو اى Function بدون return
--لمبرمجى السى/++ والجافا اى Function ال return بتاعها void يبقة اسمها Procedure
دوال جاهزة
بايثون بتقدم ليك العديد من ال Functions الجاهزة مثل raw_input(prompt) للحصول على الداتا من المستخدم input(prompt) زى ماقلنا هى بتستدعى التالى
eval(raw_input(prompt))
eval(expression)
بتحقق قيمة ال expression
abs(number) بتعيد ليك ال Absolute value -القيمة المطلقة- وهى العدد بدون اشارة
>>> abs(10) 10 >>> abs(-10) 10
- تدريب:
اكتب Function بإسم getABS وبتاخد argument واحدة بإسم number ومشابهه ل abs
max(iterable) هى function بتاخد container فى الغالب -اى شئ ممكن يطبق عليه foreach- وتعيد اكبر قيمة فيه كالتالى مثلا
>>> max([3, 4, 5, 6]) 6 >>> max("Ahmed") 'm'
min(iterable)
هى العكس من max وهى بتعيد اصغر قيمة
>>> min("Ahmed") 'A' >>> min([3, 4, 5, 6]) 3
ملحوظة: اصغر قيمة للأحرف بتم بناء على قيمتها فى ASCII وتقدر تحصل عليها من خلال ord
وللحصول على الحرف من خلال القيمة بنستخدم chr
ord(char)
>>> ord("A") 65 >>> ord("a") 97 >>> chr(65) 'A' >>> chr(97) 'a'
sum(seq)
بتستخدم للحصول على مجموع sequence ما كالتالى
>>> sum([1, 2, 3, 4, 5]) 15
- تدريب اكتب function مشابهه ل sum وبإسم getSum وبتاخد sequence ك argument
oct(num) بتعيد القيمة من النظام الثمانى لل num
>>> oct(15) '017'
hex(num)
بتعيد القيمة من النظام الست عشرى لل num
>>> hex(15) '0xf'
len(object)
فى الواقع len استخدامها بيختلف حسب نوع ال argument اللى هتتمرر ليها يعنى مثلا إذا كان string هيتم اعادة عدد الحروف وإذا كانت list هيتم إعادة عدد العناصر المكونة ليها وهكذا
>>> len("Ahmed") 5 >>> len([1, 2, 3, 4, 5, 6]) 6
هنتعلم قريب ازاى نحدد الطريقة اللى هتتعامل بيها len مع ال objects بتاعتنا :D
round(f_num, digits)
هى function بتعمل تقريب ل f_num بعدد معين من الأرقام بيساوى digits كالتالى مثلا
>>> round(2678.367789) 2678.0 >>> round(2678.367789, 4) 2678.3678
copyright()
>>> copyright() Copyright (c) 2001-2008 Python Software Foundation. All Rights Reserved. Copyright (c) 2000 BeOpen.com. All Rights Reserved. Copyright (c) 1995-2001 Corporation for National Research Initiatives. All Rights Reserved. Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. All Rights Reserved.
بتعرضلك ل copyright الخاص ببيثون credits() بتعرضلك ال credits
>>> credits() Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands for supporting Python development. See www.python.org for more information.
range(end) بتعيد ليك list من 0 لحد end بزيادة قيمتها 1
>>> range(10) #0 to 10 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(start, end)
بتعيد ليك List من start لحد end بزيادة قيمتها 1
>>> range(1, 5) #1 to 5 [1, 2, 3, 4]
range(start, end, step) بتعيد ليك list من start لحد end بزيادة مقدراها step
>>> range(1, 20, 3) #1 to 20 with step=3 [1, 4, 7, 10, 13, 16, 19]
- تدريب اكتب function تقوم بعرض جدول ال ASCII
استخدام *args
عنوان عجيب مثال على دالة sum
def mysum(alist): total=0 for i in alist: total += i return total
لإستخدامها هنمرر list من الأرقام الي الدالة ك argument كالتالى
print mysum([1, 2, 3, 4, 5])
مثال بإستخدام *args
def newsum(*args): total=0 for i in args: total +=i return total
لإستخدامها: هنمرر الأرقام ك arguments للدالة كالتالى
print newsum(1, 2, 3, 4, 5)
انشاء دالة مشابة ل printf
def printf(fmt, *args): print fmt%args printf("Name: %s, Age: %d", "Ahmed", 20) #converted to print "Name %s, Age: %d"%("Ahmed", 20)
لاحظ ان هيتم التحويل *args إلى دالة print لإستبدال ال place holders (%s, %d) بالقيم المدخلة
- تدريب "اكتب sprintf بإستخدام بايثون
استخدام **kwargs
بتمرر مفاتيح keys للدالة مثال
def newprintf(fmt, *args, **kwargs): print "fmt: ", fmt print "*args: ", args print "**kwargs: ", kwargs #do something usefu if kwargs.has_key("verbose"): print "Verbose..." newprintf("This is some FMT", 1, 2, 3, 4, 5, 6, verbose=True, cleanup=True, use_ssl=False
الناتج
fmt: This is some FMT *args: (1, 2, 3, 4, 5, 6) **kwargs: {'use_ssl': False, 'cleanup': True, 'verbose': True} Verbose...
توثيق الدالة
انك توثق الدالة وبتعمل اية وبتاخد معاملات ايه شئ ممتاز فى اى برنامج لان اهمية التوثيق من اهمية الكود
def newprintf(fmt, *args, **kwargs): """Simple function to learn out howto use *, ** trick in functions.""" print "fmt: ", fmt print "*args: ", args print "**kwargs: ", kwargs #do something usefu if kwargs.has_key("verbose"): print "Verbose..."
لاحظ ان اول سطر بعد التعريف بيعبر عن ال doc او وثيقة الدالة (ملف المساعدة بتاعها:d )
تقدر تستدعى ال .__doc__ من الدالة ليعرضلك جزئية المساعدة الخاصة بيها او تستدعى دالة help عليها .__doc__
print newprintf.__doc__ #STR #output: Simple function to learn out howto use *, ** trick in functions. Help on function newprintf in module __main__:
help
help(newprintf) #output newprintf(fmt, *args, **kwargs) Simple function to learn out howto use *, ** trick in functions.
متغيرات عامة
على فرض ان عندنا متغيرات عامة فى الملف بتاعنا ومتاجين نستخدمها فى دالة معينة هنعمل اية ؟ استخدم global كالتالى
__DEBUG=True def isdebug(): global __DEBUG return __DEBUG print "Debug? ", isdebug() #output: Debug? True
كل المطلوب انك تعرف الدالة اللى هتستدعى فيها متغيرك العام بإنه global بإستخدام global ويليها اسم المتغير
او تقدر تستخدم globals() كقاموس للمتغيرات العامة كالتالى مثلا
__DEBUG=False def isdebug(): x, y, z=range(3) print locals() return globals()["__DEBUG"] print isdebug() #output: #{'y': 1, 'x': 0, 'z': 2} #False
لاحظ استخدام locals() هنا بتعيد قاموس ايضا بيعبر عن المتغيرات المحلية فى سياقها مثل x,y,z محليين فى السياق الموجودة فيه وهو الدالة isdebug
الدوال المجهولة و lambda
بايثون بتتيحلك استخدام الدوال المجهولة ودا باستخدام lambda مستعارة من لغة lisp
def getName(name): return name anonyFunc=lambda name: name print anonyFunc("Mido") print getName("Mido") #Output: #Mido #Mido
مابعد lambda هو ال args ومايليها هو ال return
def getSum(*args): return sum(args) anonySum=lambda *args: sum(args) print getSum(1, 2, 3, 4, 5) print anonySum(1, 2, 3, 4, 5) #Output: 15 15
map
بتطبق function معينة على مجموعة من العناصر
print map(lambda w: w.upper(), ["ahmed", "mostafa", "omar"])
هنا هيتعمل ريترن بنسخة من العناصر بعد التعديل (التحويل للحروف الكبيرة)
['AHMED', 'MOSTAFA', 'OMAR']
استخدام lambda مش واضح بالنسبة ليك مش مشكلة استبدلها كالتالى
def toupper(w): return w.upper() users=["ahmed", "mostafa", "omar"] print map(toupper, users) #output: #['AHMED', 'MOSTAFA', 'OMAR']
صحيح ايه ال w اللى كانت فى lambda وموجود فى toupper ك parameter دى ؟
ال w دى بتعبر عن كل عنصر فى ال sequence هتطبق عليه الدالة
filter
بنستخدمها لتصفية sequence معينة هنشوف مثال
numbers=range(20) print filter(lambda i: i&1, numbers) #odds. print filter(lambda i: not i&1, numbers) #evens #output: #[1, 3, 5, 7, 9, 11, 13, 15, 17, 19] #[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
لاحظ هنا فى المثال عندنا مجموعة ارقام من 0 ل 19 هنستخدم قاعدة n & 1 لإختبار هل الرقم فردى او زوجى (او اى طريقة تعجبك) زى ماشايفين فى المثال بإستخدام lambda وبالفعل بيتم التصفية بناءا على القاعدة اللى حاطينها فى الfunction
المثال بدون استخدام lambda
def isodd(i): if i&1: return True return False def iseven(i): return not isodd(i) print filter(isodd, numbers) #odds print filter(iseven, numbers)#evens #output: #[1, 3, 5, 7, 9, 11, 13, 15, 17, 19] #[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
اماكن استخدام lambda ؟ يفضل تستخدمها مع ال properties كبديل لل getters (تابع الفصل القادم او فى دالة ب سطر واحد)
