تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
المجموعات set في بيثون
#1
 المجموعات set
المجموعات قريبة من القواميس من ناحية امكانية اجراء اختبار الانتماء او الوصول الى مختلف هناصر المجموعة او تعديلها او حذفها، كل هذا بشكل مستقل عن عدد العناصر. المجموعات هي أيضا كائنات قابلة للتعديل، لكن على خلاف القواميس، لا تخزن المجموعات سوى مفاتيح فريدة أي ليس هناك قيم تقابلها. يمكن التساؤل هنا: وما فائدة المجموعات إذا؟

في الواقع، تم تحسين أداء المجموعات وقد وقع تطويرها من أجل غايات محددة، من بينها الاحتفاظ فقط بالعناصر الفريدة غير المكررة من متتالية، حيث لو قمنا بتأليف مجموعة من عناصر متتالية سنحصل فقط  على عناصر فريدة غير مكررة. مهمة ثانية يتم فيها استخدام المجموعات بكثرة وهي اجراء اختبارات انتماء على عناصر متتالية.

- المجموعات هي كائنات قابلة للتعديل بمعنى امكانية تحريرها دون نسخ لكنها تتكون من عناصر hashable أي غير قابلة للتعديل. في المجموعات يمكننا إذا حفظ أعداد، وسلاسل نصية وصفوف (بشرط عدم تكونها من عناصر قابلة للتعديل)، لكن لا يمكننا حفظ الكائنات القابلة للتعديل ومنها القوائم والقواميس والمجموعات.

## تكوين مجموعة

كود :
s = set()                       # تكوين مجموعة فارغة
type(s)                         # set
s = {1, 2, 3, 'a', 18.5, False} # لاحظ أنه لا يمكننا تكوين مجموعة فارغة بواسطة {} فارغين
type(s)                         # والسبب ان بيثون سيعتبر الـ{} قاموسا فارغا
ما الفرق هنا بين القوسين المعقفين المستخدمين في القواميس والقوسين المعقفين الخاصين بالمجموعات؟ ببساطة في المجموعات ليس هناك علامة النقطتين : اللتين تفصلان بين المفتاح وقيمته:

كود :
d = {'A':65, 'B':66, 'C':67}
type(d)                         # dict
set(d)                          # {'A', 'B', 'C'}
يمكننا كذلك تكوين مجموعة انطلاقا من متتالية:

كود :
a = [1, 2, 4, 1, 18, 30, 4, 1]
set(a)                          # {1, 2, 4, 18, 30}
رغم أن المجموعة قابلة للتعديل غير أن جميع عناصر المجموعة يجب ان تكون غير قابلة للتعديل:

كود :
ss = {(1, 2, (3, 4))}           # Correct
ss = {(1, 2, [3, 4])}           # Error: unhashable type: 'list'
ss = {{1, 2}}                   # Error: unhashable type: 'set'
## معالجة المجموعات

### الدالة len

كود :
len(s)                          # للحصول على عدد العناصر
### اختبار الانتماء بواسطة in

كود :
'a' in s                          # True
'b' in s                          # False
### الوظيفة add

كود :
s.add('Zaid')                     # اضافة عنصر
print(s)                          # {True, 2, 3, 'a', 18.5, 'Zaid'}
### الوظيفة update

تقوم هذه الوظيفة بتحيين مجموعة وهي لا تضيف سوى العناصر غير الموجودة مسبقا:

كود :
s.update([1, 1, 1, 2, 3, 4, 5, 5, 6, 'Zaid', 'Amr'])
print(s)                          # {False, 1, 2, 3, 4, 5, 6, 'a', 'Amr', 18.5, 'Zaid'}
### الوظيفة discard

كود :
s.discard('Zaid')                 # حذف عنصر
s                                 # {False, 1, 2, 3, 4, 5, 6, 'a', 'Amr', 18.5}
s.discard('foo')                  # الوظيفة تعمل حتى لو كان العنصر المراد حذفه غير موجود
### الوظيفة remove

كود :
s.remove('Amr')                   #  حذف عنصر
s.remove('foo')                   # هنا سيطلق بيثون استثناء
try:
   s.remove('foo')
except KeyError as e:
   print("remove raised exception:", e)
   
الفرق بين discard و remove هو الرغبة او عدم الرغبة في التأكد من وجود العنصر قبل محاولة حذفه

### الوظيفة pop

رأينا هذه الوظيفة على المتتاليات، حيث تحذف وترد لنا العنصر الذي قدمنا مؤشره كوسيط. بدون وسيط تحذف pop العنصر الأخير وترده لنا. تذكير:

كود :
myList = list(s)                  # تحويل المجموعة الى قائمة
myList                            # [False, 1, 2, 3, 4, 5, 6, 'a', 18.5]
myList.pop(7)                     # 'a' حذف العنصر
myList                            # [False, 1, 2, 3, 4, 5, 6, 18.5]
myList.pop()                      # 18.5
myList                            # [False, 1, 2, 3, 4, 5, 6]
لكن بالنسبة للمجموعات، لا وجود لمؤشر وليس هناك ترتيب محدد للعناصر داخل المجموعة، لذلك فهي تحذف عنصرا بشكل عشوائي وترده لنا، أما اذا كانت المجموعة فارغة فيطلق بيثون استثناء:

كود :
try:
   s.pop()
except KeyError as e:
   print(e)
### الوظيفة clear

تحذف جميع العناصر دفعة واحدة

كود :
s.clear()
s
### العمليات على المجموعات

كود :
s1 = {1, 2, 3}
s2 = {3, 4, 5}
s1 - s2                         # difference
s1 | s2                         # union
s1 & s2                         # intersection
s1 == s2                        # False
s1 <= s2                        # inclusion
s1 < s2                         # inclusion
## أهمية وفاعلية المجموعات في عمليات البحث

عندما يرغب المبرمج في اجراء عملية بحث في مجموعة كبيرة من البيانات، لابد أن يتساءل عن أفضل طريقة لربح الوقت وللتقليل من استهلاك موارد النظام، الخ... سنجري مقارنة بين عملية بحث داخل قائمة واخرى داخل مجموعة (هنا استخدمت مفسر ipython):

كود :
a = [0]                         # قائمة ليس بها سوى عنصر واحد
s = set(a)                      # مجموعة انطلاقا من القائمة
%timeit -n 50 0 in a            # تكرار 50 مرة على القائمة
%timeit -n 50 0 in s            # تكرار 50 مرة على المجموعة
عندما نبحث عن عنصر في متتالية، نضطر لمقارنة عناصرها واحدا بعد الآخر بما نبحث عنه حتى نجد النتيجة. في أسوإ الحالات أي عندما تكون نتيجة البحث سلبية، نكون قد أجرينا عمليات مقارنة بين جميع عناصر المتتالية وما نبحث عنه.

عملية البحث في المجموعات تتبع تمشيا مختلفاتماما : باعتبار المجموعات مبنية على جدول هاش (كالقواميس) فالبحث يتمثل في عمليات حسابية تقوم بها دالة هاش. دالة الهاش  في المجموعة لا تستهلك عمليا سوى وقت الوصول الى  عنصر واحد من عناصر القائمة، نستنتج من ذلك أن تحويل القائمة الى مجموعة قبل عملية المقارنة مفيد مهما كان حجم القائمة، لكنك قد تتساءل كم نحتاج من الوقت لتحويل متتالية الى مجموعة؟ سيحتاج التحويل الى الوقت الذي تحتاجه دالة الهاش في عملياتها على كل عنصر وهو اساسا الوقت اللازم لتصفح المتتالية مرة واحدة اذا أجريت بحثا عن كائن غير موجود في المتتالية، وبالتالي يبقى التحويل الى مجموعة أفضل وأكثر مردودية عند الرغبة في اختبار انتماء عنصر الى متتالية.

إذا كنت تستخدم ipython يمكنك أن تجرب هذين المثالين كل على حده لتقارن الوقت: بالنسبة لي استغرق انشاء المجموعة والبحث داخلها نصف الوقت الذي استغرقه البحث في القائمة رغم أنه تم انشاؤها مسبقا (تحذير: احفظ ملفاتك قبل التجربة، قد يتسبب السكربت في توقف الجهاز عن الاستجابة):



كود :
l = [i for i in range(50000000)]    # قائمة كبيرة جدا

%%timeit -n 1
for i in range(49999990,50000000):
   if i in l:
       print(i, "in list")
كود :
%%timeit -n 1
s = set(l)
for i in range(49999990,50000000):
   if i in s:
       print(i, "in set")
## الدالة frozenset

المجموعات هي أنواع قابلة للتعديل كما رأينا أعلاه، لذلك لا يمكننا تكوين مجموعة من المجموعات. لهذا السبب تم بناء الدالة frozenset. هذه الدالة تنشئ لنا مجموعة غير قابلة للتعديل يمكننا بالتالي استخدامها كمفتاح في قاموس او مجموعة فرعية داخل مجموعة. لا يوجد طريقة مختصرة لانشائها عبر اقواس مثلا، ولابد من استخدام frozenset. من بين الوظائف المستبعدة في frozenset يمكننا ذكر update - pop - clear - remove - discard. كل هذه الوظائف جُعلت لاجراء التعديلات على المجموعات (وغيرها) وهي بالتالي غير متوفرة في frozenset.

كود :
fset = frozenset()                  # فارغة
fset                                # frozenset()
type(fset)
vowels = ('a', 'e', 'i', 'o', 'u')  # frozenset صف سنحوله الى
fset = frozenset(vowels)
fset                                # frozenset({'a', 'e', 'i', 'o', 'u'})
d = {'A':65, 'B':66, 'C':67}        # frozenset قاموس سنحوله الى
fset = frozenset(d)
fset                                # frozenset({'A', 'B', 'C'})
## للمزيد من المعلومات عن set و frozenset

https://docs.python.org/3/library/stdtyp...-frozenset
الرد


التنقل السريع :


مستخدمين يتصفحوا هذا الموضوع: 1 ضيف