Functional Python
هذا الفصل اختيارى.. هنتكلم عن functools و itertools
functools reduce(function, iterable[, init])
ملحوظة هى هى الدالة reduce
من اسمها "تقليل" الهدف منها هو تقليل sequence لقيمة واحدة
#reduce (func, seq[, init])
print reduce((lambda x, y: x+y), [1, 2, 3, 4, 5]) #15
#((((1+2)+3)+4)+5)
print reduce((lambda x, y: x*y), [1, 2, 3, 4, 5]) #120
partial(func, *args, **kwargs)
هدفها هو تغليف دالة ما ومعاملاتها واعادة كائن -مشابه للدوال- يحويها هذا المفهوم يسمى Currying
>>> def sayhi(to):
... print "Hi, ", to
>>> f=lambda to="Ahmed":sayhi(to)
>>> f
<function <lambda> at 0xb7df08b4>
>>> f()
Hi, Ahmed
نقدر نسهل نفس المثال بإستخدام partial كالتالى
>>> part_f=partial(sayhi, "Ahmed")
>>> part_f()
Hi, Ahmed
تقدر تستخدمها بطريقة انك تغلف دالة ومجموعة معاملات قد تستكمل لاحقا
>>> def someparty(*guests):
... print "Visitors: "
... for g in guests:
... print "\t", g
>>> someparty("Ahmed")
Visitors:
Ahmed
>>> someparty("Ahmed", "Mido")
Visitors:
Ahmed
Mido
>>> p=partial(someparty, "Ahmed")
>>> p("Mido")
Visitors:
Ahmed
Mido
>>> p
<functools.partial object at 0xb79d8d74>
>>> p("Mido", "Youssef")
Visitors:
Ahmed
Mido
Youssef
احد الإستخدامات العملية لها فى التعامل مع PyQt4 كالتالى مثلا -لأنك ستواجه صعوبة فى تمرير المتغيرات مع connect لل slot
self.connect(btn, SIGNAL("clicked()"),
partial(self.onClicked, ARG))
طبعا تقدر تستخدم lambda بنفس الكيفية!
هنا مثلا نقوم بربط الإشارة clicked للزر button2 بslot -تغلف self.anyButton اللتى تأخذ معامل واحد اللتى يتم تنفيذها عند محاولة استدعاء الslot- هكذا قد تغلبنا على العملية اليس كذلك ؟
itertools, Anytime! any(iterable)
عمرك كان عندك مثلا list من مجموعة ارقام وعايز تعرف هل فيها رقم اكبر من 3 مثلا ؟
print any(x>3 for x in [1, 3, 4, 5, 6, 7]) #True
all(iterable)
عمرك كان عندك مثلا list من مجموعة ارقام وعايز تعرف هل كلها اكبر من 3 مثلا ؟
print all(x>3 for x in [1, 3, 4, 5, 6, 7]) #False
الفكرة هى ان بتنشئ list من True/False ويتم حساب الناتج عن طريقها سواء على قيمة واحدة او كل القيم
لاحظ ان هنا ال iterable انشئناه بإستخدام ال list comprehension ولو اتكلمنا عنها تانى هنختصرها ل LC
repeat(obj, times=None)
بتقوم بعمل iterator يعيد نفس الكائن بعدد times او للأبد اذا كان None
>>> list(repeat(10, 3))
[10, 10, 10]
هنلاحظ ان الناتج [10, 10, 10] تم تكرار الكائن10 ل 3 مرات
وهكذا
takewhile(pred, seq)
بتأخذ عناصر من seq كلما يتم تأكيد الشرط المؤكد pred
>>> list(takewhile(lambda x: x<3, [0, 1, 2, 3, 4]))
[0, 1, 2]
dropwhile(pred, seq)
العكس بقة بتترك عناصر من seq كلما يتم تأكيد ال pred
>>> list(dropwhile(lambda x: x<3, [0, 1, 2, 3, 4, 6, 7]))
[3, 4, 6, 7]
groupby(seq[, key=None])
على فرض عندنا سلسلة من العناصر وعايزين نجمع كل العناصر من seq بإستخدام key function افتراضيا ال key هو العنصر- lambda x: x-
>>> list(groupby("111122233344"))
[('1', <itertools._grouper object at 0xb77ef36c>), ('2', <itertools._grouper object at 0xb77ef3ec>), ('3', <itertools._grouper object at 0xb77ef40c>), ('4', <itertools._grouper object at 0xb77ef42c>)]
print [list(i) for (x,i) in itertools.groupby("111122233344")]
الناتج
[['1', '1', '1', '1'], ['2', '2', '2'], ['3', '3', '3'], ['4', '4']]
وهكذا
الفكرة ان groupby بتجمع العناصر وبتعيدها فى صورة العنصر وiterator ليه و هكذا) فعشان كدا عملنا list I اذا عايز العناصر فقط مش list خد قيمة ال x بس
مثال اخر
things = [("animal", "bear"), ("animal", "duck"), ("plant", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]
for key, group in groupby(things, lambda x: x[0]):
for thing in group:
print "A %s is a %s." % (thing[1], key)
print " "
الناتج
A bear is a animal.
A duck is a animal.
A cactus is a plant.
A speed boat is a vehicle.
A school bus is a vehicle.
مرجع للمثال
http://stackoverflow.com/questions/773/how-do-i-use-pythons-itertoolsgroupbychain(*iters)
ينشئ iterator ليعيد العناصر من اول عنصر يتم تمريره حتى ينتهى فينتقل للعنصر التالى وهكذا
او تقدر تقول بتنشئ سلسلة بين ال iters
>>> for el in chain("Hello", "World", range(3)):
... print el
H
e
l
l
o
W
o
r
l
d
0
1
2
ifilter(pred, seq)
نفس فكرة filter ..تعيد iterator فى لكل عنصر يقع فى نطاق ال pred
>>> for x in ifilter(lambda x: x>3, [3,2, 1, 9, 8]): print x
...
9
8
>>> list(ifilter(lambda x: x>3, [3,2, 1, 9, 8]))
[9, 8]
filter=ifilter معادا فى كيفية اعادة الناتج
>>> filter(lambda x: x>3, [3, 2, 1, 9, 8])
[9, 8]
حيث تعيد filter ناتج من list بينما تعيد ifilter ناتج من iterator
ifilterfalse(pred, seq)
حيث تعيد iterator لكل عنصر لايقع فى نطاق pred
>>> list(ifilterfalse(lambda x: x>3, [3, 2, 1, 9, 8]))
[3, 2, 1]
izip(*iters)
خمن؟ ايوه صح هى فعلا مشابهه ل zip ولكن بتعيد iterator
>>> list(izip("Hell", [1,2,3, 4]))
[('H', 1), ('e', 2), ('l', 3), ('l', 4)]
>>> zip("Hell", [1,2,3,4])
[('H', 1), ('e', 2), ('l', 3), ('l', 4)]
للمزيد راجع وثائق python حولهم