# Sets and Frozen sets in Python

##### Introduction

Sets in Python is an unordered collection with no duplicate elements. Since sets are unordered, we cannot access items using indexes as we do in lists. It is a mutable object that can be modified. But there is another object type called frozen sets that is immutable.

Basic uses of sets include membership testing and eliminating duplicate entries. Moreover, its objects support mathematical operations like union, intersection, difference, and symmetric difference. Python’s set class represents the mathematical notion of a set.

**How to create sets in python?**

Curly braces or the **set()** function can be used to create sets and commas to separate its elements.

Example 1:

```
x = {1,2,3.7,'sets'}
print(x)
print(type(x))
# Output:
{'sets', 1, 2, 3.7}
<class 'set'>
```

Example 2 (*creating empty set*):

To create an empty set you have to use constructor **set()**. If you use **empty curly braces {} **to declare an empty set, It will create an empty dictionary because the **dictionary **is key-value pairs enclosed inside the curly braces.

```
empty_set = {} # Wrong way to declare empty set
empty_set1 = set() # Right way to declare empty set
print(f"Data type of empty_set:{type(empty_set)}")
print(f"Data type of empty_set1:{type(empty_set1)}")
# Output
Data type of empty_set:<class 'dict'>
Data type of empty_set1:<class 'set'>
```

Example 3 (*Sets that contain different immutable types of element*):

```
fruits = {'Mango', 'Apple','Banana', 10, 5.5}
print(fruits)
# Output:
{5.5, 'Apple', 10, 'Mango', 'Banana'}
```

Example 4 (*Sets containing different types of data including mutable data types*):

```
sets = {'Mango', 'Apple','Banana', 10, 5.5, [2, 4.5, 'Python']}
print(sets)
```

Error message:

Traceback (most recent call last):

File “C:\Users\Dawa Penjor\Desktop\test.py”, line 1, in

sets = {‘Mango’, ‘Apple’,’Banana’, 10, 5.5, [2, 4.5, ‘Python’]}

TypeError: unhashable type: ‘list’

*Example 4 runs into an error because sets do not allow mutable data types. We know It can take in different data types but it should be an immutable type. [2, 4.5, ‘Python’] is a mutable list*.

Python sets are mutable because we can add and delete elements into them, but they can’t contain mutable items. Like we have seen before, the set can’t contain mutable lists.

**Why sets in python are mutable but can’t contain mutable items?** Because set internally uses a hash table to store its elements so for that set elements need to be hashable; mutable elements like the list are not hashable.

Note:**Mutable** elements are not hashable.**Immutable** elements are hashable.

**Set does not accept duplicate element**

A set is an unordered collection with no duplicate elements. This becomes very useful when we deal with an email address, Identity number, phone number, etc… which is not repeated.

Example:

```
email_address = {'xyz@gmail.com', 'dawa@gmail.com', 'bht@gmail.com', 'xyz@gmail.com'}
print(email_address)
# Output:
# Duplicate address (xyz@gmail.com) is removed
{'xyz@gmail.com', 'bht@gmail.com', 'dawa@gmail.com'}
```

**Set does not support indexing**

Since the set is an unordered data type, indexing won’t work.

Example:

```
fruits = {'mango', 'apple', 'Guava'}
print(fruits[1]) # looking for an element at index 1 in fruits
# Error message:
TypeError: 'set' object is not subscriptable
```

**Iteration over a set**

We can iterate over a set using a **For** loop.

Example:

```
fruits = {'mango', 'apple', 'Guava'}
for i in fruits:
print(i)
# Output
apple
Guava
mango
```

**Membership check in sets**

We can check the element of the set using a **membership operator**.

Example:

```
fruits = {'mango', 'apple', 'Guava'}
if 'mango' in fruits:
print("mango is in fruits list")
else:
print("fruit not found!")
# Output
mango is in fruits list
```

**Functions that can be used with sets:**

The built-in functions such as** len()**,

**, and**

*min()***can be used even in set data type.**

*max()*Example:

```
num = {10, 20, 30, 1, 5}
print("Length of the set=",len(num))
print("Minimum value=", min(num))
print("Maximum Value=",max(num))
# Output:
Length of the set= 5
Minimum value= 1
Maximum Value= 30
```

**Sets method**:

**add()**

Adds a new element to a set. This does not affect if the element is already present.

Example:

```
s = {'2020', 'Python', 'coder'}
s.add('Bhutan')
print(s)
# Output:
{'Bhutan', 'Python', 'coder', '2020'}
```

**clear()**

Removes all elements from the set.

Example:

```
s = {'2020', 'Python', 'coder'}
s.clear()
print(s)
# Output:
set()
```

**copy()**

Returns a shallow copy of a set.

Example:

```
s = {5, 10, 10.5, 'Python'}
s1 = s.copy()
print(s)
print(s1)
# Output:
{'Python', 10, 10.5, 5}
{'Python', 10, 10.5, 5}
```

**difference()**

Returns the difference of two or more sets as a new set.

Let’s say that **set1 (s1)** contains {1, 2, 3, 4, 5} and **set2 (s2)** contains {4, 5, 6, 7, 8, 9}

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
print(s1.difference(s2))
# Output:
{1, 2, 3} # Elements that are in s1 but not in s2
```

We can also use the minus (-) operator to display the difference of the set. But when we use the operator, both must be a set.

Example:

```
# Example 1
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
print(s1 - s2)
# output:
{1, 2, 3}
# Example 2:
s1 = {1, 2, 3, 4, 5}
print(s1.difference([4, 5, 6, 7, 8, 9]))
print(s1 - [4, 5, 6, 7, 8, 9])
# Output:
{1, 2, 3}
Traceback (most recent call last):
File "C:\Users\Dawa Penjor\Desktop\test.py", line 3, in <module>
print(s1 - [4, 5, 6, 7, 8, 9])
TypeError: unsupported operand type(s) for -: 'set' and 'list'
```

**difference_update()**

Removes all elements of another set.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
s1.difference_update(s2)
print(s1)
# Output:
{1, 2, 3} # All elements of s2 has been removed
```

**symmetric_difference()**

Returns the difference of two sets. ie the elements that are in either of the sets, but not in both.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
print(s1.symmetric_difference(s2))
# Output:
{1, 2, 3, 6, 7, 8, 9} # Removed 4 and 5 which are in both sets
```

We can also write using signs. Use **^** to denote symmetric_difference.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
print(s1 ^ s2)
# Output:
{1, 2, 3, 6, 7, 8, 9}
```

**symmetric_difference_update()**

Update sets with the symmetric difference of itself and another.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
s1.symmetric_difference_update(s2)
print(s1)
# Output:
{1, 2, 3, 6, 7, 8, 9}
```

**discard()**

Removes an element from a set if it is a member. If the element is not a member, do nothing.

Example:

```
s = {5, 10, 10.5, 'Python'}
s.discard(10)
print(s)
Output:
{'Python', 10.5, 5}
```

**intersection()**

This method returns a set that contains the elements that exist in both sets, or all sets if the method is used with more than two sets.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
print(s1.intersection(s2))
# Output:
{4, 5}
```

We can also write the intersection of set using **&**.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
print(s1 & s2)
# Output:
{4, 5}
```

**intersection_update()**

Updates a set with the intersection of itself and another.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
s1.intersection_update(s2)
print(s1)
# Output:
{4, 5}
```

**isdisjoint()**

Returns **True **if two sets have a null intersection or do not have common elements. If not it returns **False**.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
s3 = {10, 11, 12}
print(s1.isdisjoint(s2))
print(s1.isdisjoint(s3))
# Output:
False # Because s1 contains 4 and 5 which is also in s2
True # Because s1 does not contain any element which is in s3
```

**issubset()**

Returns **True **or **False **depending on whether another set contains all the elements of other set.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
s3 = {1, 2, 3, 4, 5, 6, 7, 8, 9}
print(s1.issubset(s2))
print(s1.issubset(s3))
Output:
False # s2 does not contain all the element of s1. Therefore, s1 is not the subset of s2.
True # s3 contains all the element of s1. Therefore, s1 is subset of s3.
```

**issuperset()**

This method is similar to subset.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
s3 = {1, 2, 3, 4, 5, 6, 7, 8, 9}
print(s3.issuperset(s1))
print(s2.issuperset(s1))
# Output:
True # s3 contains all the element of s1
False # s2 doesnot contain all the element of s1
```

**pop()**

Removes and returns an arbitrary set element. Raises **KeyError **if the set is empty.

Example:

```
s = {5, 10, 10.5, 'Python'}
s.pop()
print(s)
# Output:
{10.5, 5, 'Python'}
```

**remove()**

Removes an element from a set; it must be a member. If the element is not a member, raises a **KeyError**.

Example:

```
s = {5, 10, 10.5, 'Python'}
s.remove(10)
print(s)
# Output:
{10.5, 5, 'Python'} # Number 10 is removed
```

**union()**

The union of two or more sets is the set of all unique elements present in all the sets. We can also use pipe **(|)** operator for union.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
print(s1.union(s2))
print(s1 | s2) # Union of set using operator
# Output:
{1, 2, 3, 4, 5, 6, 7, 8, 9}
{1, 2, 3, 4, 5, 6, 7, 8, 9}
```

**update()**

Updates a set with the union of itself and others.

Example:

```
s1 = {1, 2, 3, 4, 5}
s2 = {4, 5, 6, 7, 8, 9}
s1.update(s2)
print(s1)
# Output:
{1, 2, 3, 4, 5, 6, 7, 8, 9}
```

**Frozen set**s

**Frozenset **are immutable sets. They have the same properties as **set **except they cannot be modified or mutated. Therefore, all methods that don’t modify the set is also available with frozenset.

Frozensets can be used as keys in dictionaries or as element in another set or frozenset.

To create a frozenset we use its constructor.

Example :

```
fs = frozenset()
print(fs)
print(type(fs))
# Output:
frozenset()
<class 'frozenset'>
```

Example 2:

```
fs = frozenset([3, 4, 5.6, 'python'])
print(fs)
# Output:
frozenset({'python', 3, 4, 5.6})
```