ال

المجموعات في Python

16 تمرينًا

نبذة عن المجموعات

‏set تجميعة قابلة للتغيير و_غير مرتبة_ من كائنات قابلة للتجزئة. ويجب أن تكون عناصر المجموعة متمايزة، فالعناصر المكررة غير مسموح بها. ويمكنها أن تحمل أنواع بيانات مختلفة ومتعددة، وحتى بنى متداخلة مثل tuple من tuples، ما دام يمكن تجزئة جميع العناصر. كما تتوفر المجموعات بنمط غير قابل للتغيير هو frozenset.

يُكثر استخدام المجموعات لإزالة العناصر المكررة بسرعة من بنى البيانات الأخرى أو من تجميعات العناصر. وتُستخدم أيضاً لإجراء مقارنات فعّالة عندما لا تكون هناك حاجة إلى التسلسل أو تتبّع المكررات.

ومثل أنواع التجميعات الأخرى (القواميس والمصفوفات والصفوف)، تدعم sets ما يلي:

  • التكرار عبر for item in <set>،
  • التحقق من العضوية عبر in وnot in،
  • حساب الطول عبر len()، و
  • النسخ السطحي عبر copy().

ولا تدعم sets ما يلي:

  • الفهرسة بأي شكل
  • الترتيب عبر الفرز أو الإدراج
  • التقطيع
  • الدمج عبر +

التحقق من العضوية في set له تعقيد زمني ثابت (في المتوسط) مقارنةً بالتحقق من العضوية في list أو string، حيث ينمو التعقيد الزمني مع زيادة طول البيانات. كما أن طرقاً مثل <set>.union() أو <set>.intersection() أو <set>.difference() لها تعقيد زمني ثابت (في المتوسط).

إنشاء المجموعات

يمكن إنشاء المجموعات بطرق مختلفة كثيرة، لكن أبسط طرق الإنشاء هي الإعلان عن مجموعة حرفية، أو استخدام باني صنف set (set())، أو استخدام استيعاب المجموعة.

المجموعات الحرفية

يمكن إدخال set مباشرةً كـ_مجموعة حرفية_ باستخدام الأقواس المعقوفة {} وفواصل بين العناصر. وتُحذف العناصر المكررة بصمت:

>>> one_element = {'➕'}
{'➕'}

>>> multiple_elements = {'➕', '🔻', '🔹', '🔆'}
{'➕', '🔻', '🔹', '🔆'}

>>> multiple_duplicates =  {'Hello!', 'Hello!', 'Hello!', 
                            '¡Hola!','Привіт!', 'こんにちは!', 
                            '¡Hola!','Привіт!', 'こんにちは!'}
{'こんにちは!', '¡Hola!', 'Hello!', 'Привіт!'}

تستخدم المجموعات الحرفية الأقواس المعقوفة نفسها التي تستخدمها قيم dict الحرفية، وهذا يعني أنك تحتاج إلى استخدام set() لإنشاء set فارغة.

باني المجموعة

يمكن استخدام set() (الباني الخاص بصنف set) مع أي iterable يُمرَّر كوسيط. ويتم المرور على عناصر iterable وإضافتها إلى set واحداً واحداً. ولا يُحتفظ بترتيب العناصر، وتُحذف العناصر المكررة بصمت:

# To create an empty set, the constructor must be used.
>>> no_elements = set()
set()

# The tuple is unpacked & each element is added.  
# Duplicates are removed.
>>> elements_from_tuple = set(("Parrot", "Bird", 
                               334782, "Bird", "Parrot"))
{334782, 'Bird', 'Parrot'}

# The list is unpacked & each element is added.
# Duplicates are removed.
>>> elements_from_list = set([2, 3, 2, 3, 3, 3, 5, 
                              7, 11, 7, 11, 13, 13])
{2, 3, 5, 7, 11, 13}

استيعاب المجموعات

وكما هو الحال مع lists وdicts، يمكن إنشاء المجموعات عبر الاستيعاب:

# First, a list with duplicates
>>> numbers = [1,2,3,4,5,6,6,5,4,8,9,9,9,2,3,12,18]

# This set comprehension squares the numbers divisible by 3
# Duplicates are removed.
>>> calculated = {item**2 for item in numbers if item % 3 == 0}
{9, 36, 81, 144, 324}

أخطاء شائعة عند إنشاء المجموعات

نظراً لسلوكه في "فك التغليف"، قد يكون استخدام باني set مع سلسلة نصية مفاجئاً:

# String elements (Unicode code points) are 
# iterated through and added *individually*.
>>> elements_string = set("Timbuktu")
{'T', 'b', 'i', 'k', 'm', 't', 'u'}

# Unicode separators and positioning code points 
# are also added *individually*.
>>> multiple_code_points_string = set('अभ्यास')
{'अ', 'भ', 'य', 'स', 'ा', '्'}

تذكّر: يمكن للمجموعات أن تحمل أنواع بيانات مختلفة وحتى أنواعاً متداخلة، لكن يجب أن تكون جميع عناصر set قابلة للتجزئة:

# Attempting to use a list for a set member throws a TypeError
>>> lists_as_elements = {['🌈','💦'], 
                        ['☁️','⭐️','🌍'], 
                        ['⛵️', '🚲', '🚀']}

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'


# Standard sets are mutable, so they cannot be hashed.
>>> sets_as_elements = {{'🌈','💦'}, 
                        {'☁️','⭐️','🌍'}, 
                        {'⛵️', '🚲', '🚀'}}

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'

ومع ذلك، يمكن إنشاء set من sets باستخدام النوع frozenset():

# Frozensets don't have a literal form.
>>> set_1 = frozenset({'🌈','💦'})
>>> set_2 = frozenset({'☁️','⭐️','🌍'})
>>> set_3 = frozenset({'⛵️', '🚲', '🚀'})

>>> frozen_sets_as_elements = {set_1, set_2, set_3}
>>> frozen_sets_as_elements
{frozenset({'⛵️', '🚀', '🚲'}),
 frozenset({'🌈', '💦'}),
 frozenset({'☁️', '⭐️', '🌍'})}

إضافة أعضاء المجموعة وإزالتها

يمكن إضافة العناصر أو إزالتها من set باستخدام الطريقتين <set>.add(<item>) و<set>.remove(<item>). سترفع الطريقة .remove(<item>) استثناء KeyError إذا لم يكن العنصر موجوداً في set:

>>> creatures = {'crow', 'duck', 'fish', 'monkey', 'elephant'}
>>> creatures.add('beaver')
>>> creatures.remove('duck')
>>> creatures
{'beaver', 'crow', 'elephant', 'fish', 'monkey'}

# Trying to remove an item that is not present raises a KeyError
>>> creatures.remove('bear')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  KeyError: 'bear'

استراتيجيات إضافية لإزالة أعضاء المجموعة

  • ستزيل <set>.discard(<item>) عنصراً من set، لكنها لن ترفع استثناء KeyError إذا لم يكن العنصر موجوداً.
  • ستزيل <set>.clear() جميع العناصر من المجموعة.
  • ستزيل <set>.pop() عنصراً عشوائياً و_تُرجعه_، وترفع استثناء KeyError إذا كانت set فارغة.

عمليات المجموعات

للمجموعات طرق تحاكي عموماً عمليات المجموعات الرياضية. ولمعظم هذه الطرق (وليس كلها) نظير من العوامل. تأخذ الطرق عموماً أي iterable كوسيط، بينما تتطلب العوامل أن يكون طرفا العملية من نوع sets أو frozensets.

اختبار العضوية بين المجموعات

تُستخدم الطريقة <set>.isdisjoint(<other_collection>) لاختبار ما إذا كانت عناصر set تتداخل بأي شكل مع عناصر مجموعة أخرى. تقبل هذه الطريقة أي iterable أو set كوسيط. وستُرجع True إذا لم يكن بين المجموعتين أي عناصر مشتركة، وFalse إذا كانت هناك عناصر مشتركة.

# Both mammals and additional_animals are lists.
>>> mammals = ['squirrel','dog','cat','cow', 'tiger', 'elephant']
>>> additional_animals = ['pangolin', 'panda', 'parrot', 
                          'lemur', 'tiger', 'pangolin']

# Animals is a dict.
>>> animals = {'chicken': 'white',
               'sparrow': 'grey',
               'eagle': 'brown and white',
               'albatross': 'grey and white',
               'crow': 'black',
               'elephant': 'grey', 
               'dog': 'rust',
               'cow': 'black and white',
               'tiger': 'orange and black',
               'cat': 'grey',
               'squirrel': 'black'}
               
# Birds is a set.
>>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}

# Mammals and birds don't share any elements.
>>> birds.isdisjoint(mammals)
True

# There are also no shared elements between 
# additional_animals and birds.
>>> birds.isdisjoint(additional_animals)
True

# Animals and mammals have shared elements.
# **Note** The first object needs to be a set or converted to a set
# since .isdisjoint() is a set method.
>>> set(animals).isdisjoint(mammals)
False

التحقق من المجموعات الجزئية والشاملة

تُستخدم <set>.issubset(<other_collection>) للتحقق مما إذا كان كل عنصر في <set> موجوداً أيضاً في <other_collection>. وصيغة العامل هي <set> <= <other_set>:

# Set methods will take any iterable as an argument.
# All members of birds are also members of animals.
>>> birds.issubset(animals)
True

# All members of mammals also appear in animals.
# **Note** The first object needs to be a set or converted to a set
# since .issubset() is a set method.
>>> set(mammals).issubset(animals)
True

# Both objects need to be sets to use a set operator
>>> birds <= set(mammals)
False

# A set is always a loose subset of itself.
>>> set(additional_animals) <= set(additional_animals)
True

‏<set>.issuperset(<other_collection>) هي عكس .issubset(). وتُستخدم للتحقق مما إذا كان كل عنصر في <other_collection> موجوداً أيضاً في <set>. وصيغة العامل هي <set> >= <other_set>:

# All members of mammals also appear in animals.
# **Note** The first object needs to be a set or converted to a set
# since .issuperset() is a set method.
>>> set(animals).issuperset(mammals)
True

# All members of animals do not show up as members of birds.
>>> birds.issuperset(animals)
False

# Both objects need to be sets to use a set operator
>>> birds >= set(mammals)
False

# A set is always a loose superset of itself.
>>> set(animals) >= set(animals)
True

المجموعات الجزئية والشاملة «الفعلية»

تُستخدم <set> < <other_set> و<set> > <other_set> لاختبار المجموعات الجزئية الفعلية. تكون set مجموعة جزئية فعلية إذا تحقق (<set> <= <other_set>) و (<set> != <other_set>) مع العامل <.

وتكون set مجموعة شاملة فعلية إذا تحقق (<set> >= <other_set>) و (<set> != <other_set>) مع العامل >. وليس لهذه العوامل نظير من الطرق:

>>> animal_names = {'albatross','cat','chicken','cow','crow','dog',
                   'eagle','elephant','sparrow','squirrel','tiger'}

>>> animals_also = {'albatross','cat','chicken','cow','crow','dog',
                   'eagle','elephant','sparrow','squirrel','tiger'}

>>> mammals = {'squirrel','dog','cat','cow', 'tiger', 'elephant'}
>>> birds   = {'crow','sparrow','eagle','chicken', 'albatross'}

>>> mammals < animal_names
True

>>> animal_names > birds
True

# A set is not a *proper subset* if set == other set.
>>> animals_also < animal_names
False

# A set is never a *proper subset* of itself
>>> animals_also < animals_also
False

اتحاد المجموعات

تُرجع <set>.union(*<other iterables>) مجموعة set جديدة بعناصر من <set> ومن جميع <other iterables>. وصيغة العامل لهذه الطريقة هي <set> | <other set 1> | <other set 2> | ... | <other set n>.

>>> perennials = {'Asparagus', 'Broccoli', 'Sweet Potato', 'Kale'}
>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Summer Squash'}
>>> more_perennials = ['Radicchio', 'Rhubarb', 
                      'Spinach', 'Watercress']

# Methods will take any iterable as an argument.
>>> perennials.union(more_perennials)
{'Asparagus','Broccoli','Kale','Radicchio','Rhubarb',
'Spinach','Sweet Potato','Watercress'}

# Operators require sets.
>>> set(more_perennials) | perennials
{'Asparagus',
 'Broccoli',
 'Kale',
 'Radicchio',
 'Rhubarb',
 'Spinach',
 'Sweet Potato',
 'Watercress'}

فروق المجموعات

تُرجع <set>.difference(*<other iterables>) مجموعة set جديدة بالعناصر الموجودة في <set> الأصلية وغير الموجودة في <others>. وصيغة العامل لهذه الطريقة هي <set> - <other set 1> - <other set 2> - ... - <other set n>.

>>> berries_and_veggies = {'Asparagus', 
                          'Broccoli', 
                          'Watercress', 
                          'Goji Berries', 
                          'Goose Berries', 
                          'Ramps', 
                          'Walking Onions', 
                          'Blackberries', 
                          'Strawberries', 
                          'Rhubarb', 
                          'Kale', 
                          'Artichokes', 
                          'Currants'}

>>> veggies = ('Asparagus', 'Broccoli', 'Watercress', 'Ramps',
               'Walking Onions', 'Rhubarb', 'Kale', 'Artichokes')

# Methods will take any iterable as an argument.
>>> berries = berries_and_veggies.difference(veggies)
{'Blackberries','Currants','Goji Berries',
 'Goose Berries', 'Strawberries'}

# Operators require sets.
>>> berries_and_veggies - berries
{'Artichokes','Asparagus','Broccoli','Kale',
'Ramps','Rhubarb','Walking Onions','Watercress'}

تقاطع المجموعات

تُرجع <set>.intersection(*<other iterables>) مجموعة set جديدة بالعناصر المشتركة بين المجموعة set الأصلية وجميع <others> (أي المجموعة set التي يتقاطع فيها كل شيء). وصيغة العامل لهذه الطريقة هي <set> & <other set> & <other set 2> & ... & <other set n>

>>> perennials = {'Annatto','Asafetida','Asparagus','Azalea',
                 'Winter Savory', 'Broccoli','Curry Leaf','Fennel', 
                 'Kaffir Lime','Kale','Lavender','Mint','Oranges',
                 'Oregano', 'Tarragon', 'Wild Bergamot'}

>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Marjoram', 
              'Summer Squash', 'Okra','Shallots', 'Basil', 
              'Cilantro', 'Cumin', 'Sunflower', 'Chervil', 
              'Summer Savory'}

>>> herbs = ['Annatto','Asafetida','Basil','Chervil','Cilantro',
            'Curry Leaf','Fennel','Kaffir Lime','Lavender',
            'Marjoram','Mint','Oregano','Summer Savory', 
            'Tarragon','Wild Bergamot','Wild Celery',
            'Winter Savory']


# Methods will take any iterable as an argument.
>>> perennial_herbs = perennials.intersection(herbs)
{'Annatto', 'Asafetida', 'Curry Leaf', 'Fennel', 'Kaffir Lime',
 'Lavender', 'Mint', 'Oregano', 'Wild Bergamot','Winter Savory'}

# Operators require both groups be sets.
>>> annuals & set(herbs)
 {'Basil', 'Chervil', 'Marjoram', 'Cilantro'}

الفروق المتماثلة للمجموعات

تُرجع <set>.symmetric_difference(<other iterable>) مجموعة set جديدة تحتوي على العناصر الموجودة في <set> أو في <other>، لكن ليس في كليهما. وصيغة العامل لهذه الطريقة هي <set> ^ <other set>.

>>> plants_1 = {'🌲','🍈','🌵', '🥑','🌴', '🥭'}
>>> plants_2 = ('🌸','🌴', '🌺', '🌲', '🌻', '🌵')


# Methods will take any iterable as an argument.
>>> fruit_and_flowers = plants_1.symmetric_difference(plants_2)
>>> fruit_and_flowers
{'🌸', '🌺', '🍈', '🥑', '🥭','🌻' }


# Operators require both groups be sets.
>>> fruit_and_flowers ^ plants_1
{'🌲',  '🌸', '🌴', '🌵','🌺', '🌻'}

>>> fruit_and_flowers ^ set(plants_2)
{'🥭', '🌴', '🌵', '🍈', '🌲', '🥑'}
Note

ينتج عن الفرق المتماثل لأكثر من مجموعتين مجموعة set تشمل العناصر الفريدة لكل set و العناصر المشتركة بين أكثر من مجموعتين في السلسلة (التفاصيل في مقالة ويكيبيديا عن الفرق المتماثل).

وللحصول على العناصر الفريدة فقط لكل set في السلسلة، يجب تجميع التقاطعات بين جميع تركيبات المجموعتين في خطوة منفصلة، ثم إزالتها:

>>> one = {'black pepper','breadcrumbs','celeriac','chickpea flour',
           'flour','lemon','parsley','salt','soy sauce',
           'sunflower oil','water'}

>>> two = {'black pepper','cornstarch','garlic','ginger',
           'lemon juice','lemon zest','salt','soy sauce','sugar',
           'tofu','vegetable oil','vegetable stock','water'}

>>> three = {'black pepper','garlic','lemon juice','mixed herbs',
             'nutritional yeast', 'olive oil','salt','silken tofu',
             'smoked tofu','soy sauce','spaghetti','turmeric'}

>>> four = {'barley malt','bell pepper','cashews','flour',
            'fresh basil','garlic','garlic powder', 'honey',
            'mushrooms','nutritional yeast','olive oil','oregano',
            'red onion', 'red pepper flakes','rosemary','salt',
            'sugar','tomatoes','water','yeast'}

>>> intersections = (one & two | one & three | one & four | 
                     two & three | two & four | three & four)
...
{'black pepper','flour','garlic','lemon juice','nutritional yeast', 
'olive oil','salt','soy sauce', 'sugar','water'}

# The ^ operation will include some of the items in intersections, 
# which means it is not a "clean" symmetric difference - there
# are overlapping members.
>>> (one ^ two ^ three ^ four) & intersections
{'black pepper', 'garlic', 'soy sauce', 'water'}

# Overlapping members need to be removed in a separate step
# when there are more than two sets that need symmetric difference.
>>> (one ^ two ^ three ^ four) - intersections
...
{'barley malt','bell pepper','breadcrumbs', 'cashews','celeriac',
  'chickpea flour','cornstarch','fresh basil', 'garlic powder',
  'ginger','honey','lemon','lemon zest','mixed herbs','mushrooms',
  'oregano','parsley','red onion','red pepper flakes','rosemary',
  'silken tofu','smoked tofu','spaghetti','sunflower oil', 'tofu', 
  'tomatoes','turmeric','vegetable oil','vegetable stock','yeast'}
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة

تعلّم المجموعات

التدريب مقفل

افتح 4 تمارين إضافية لممارسة المجموعات