# Python Set

In Python, a set is an unordered collection of unique elements. It is defined using curly braces {} or the set() function.

Here is an example of how to create a set:

```# Creating a set using curly braces
my_set = {1, 2, 3, 4, 5}

# Creating a set using set() function
my_set = set([1, 2, 3, 4, 5])
```

Once a set is created, you can perform various operations on it such as adding or removing elements, checking if an element is present in the set, finding the intersection or union of two sets, and more.

Here are some examples of how to perform common set operations:

```# Adding an element to a set

# Removing an element from a set
my_set.remove(3)

# Checking if an element is present in a set
if 4 in my_set:
print("4 is in the set")

# Finding the intersection of two sets
set1 = {1, 2, 3}
set2 = {2, 3, 4}
intersection = set1.intersection(set2)
print(intersection) # output: {2, 3}

# Finding the union of two sets
set1 = {1, 2, 3}
set2 = {2, 3, 4}
union = set1.union(set2)
print(union) # output: {1, 2, 3, 4}
```

Sets are very useful when dealing with mathematical operations such as set theory and probability. They are also very efficient for performing operations such as finding the intersection or union of two sets.

### Adding items to the set:

You can add items to a set using the `add()` method or the `update()` method.

To add a single item to a set, you can use the `add()` method as shown in the following example:

```my_set = {1, 2, 3}
print(my_set) # output: {1, 2, 3, 4}
```

To add multiple items to a set, you can use the `update()` method with an iterable (e.g., list, tuple, set) as shown in the following example:

```my_set = {1, 2, 3}
my_set.update([4, 5, 6])
print(my_set) # output: {1, 2, 3, 4, 5, 6}
```

You can also add items from another set to an existing set using the `update()` method. In this case, the union of the two sets will be added to the original set. Here’s an example:

```my_set = {1, 2, 3}
other_set = {3, 4, 5}
my_set.update(other_set)
print(my_set) # output: {1, 2, 3, 4, 5}
```

Note that when adding items to a set, duplicates are automatically removed, since a set can only contain unique items.

### Removing items from the set:

In Python, you can remove items from a set using the `remove()` or `discard()` method.

The `remove()` method removes the specified element from the set. If the element is not present in the set, it raises a `KeyError`. Here’s an example:

```my_set = {1, 2, 3, 4, 5}
my_set.remove(3)
print(my_set) # output: {1, 2, 4, 5}

# Trying to remove an element that doesn't exist
my_set.remove(6) # raises KeyError: 6
```

The `discard()` method also removes the specified element from the set. However, if the element is not present in the set, it does not raise an error. Here’s an example:

```my_set = {1, 2, 3, 4, 5}
print(my_set) # output: {1, 2, 4, 5}

# Trying to remove an element that doesn't exist
print(my_set) # output: {1, 2, 4, 5}
```

You can also use the `pop()` method to remove and return an arbitrary element from the set. Since sets are unordered, you cannot predict which element will be removed. Here’s an example:

```my_set = {1, 2, 3, 4, 5}
popped_item = my_set.pop()
print(popped_item) # output: 1
print(my_set) # output: {2, 3, 4, 5}
```

If the set is empty, calling `pop()` raises a `KeyError`.

Finally, you can use the `clear()` method to remove all items from the set:

```my_set = {1, 2, 3, 4, 5}
my_set.clear()
print(my_set) # output: set()
```

### Python Set Operations:

In Python, sets support various operations such as union, intersection, difference, and symmetric difference. Here are some examples of these operations:

1. Union: Returns a set containing all elements from both sets, without duplicates.
```set1 = {1, 2, 3}
set2 = {3, 4, 5}
union_set = set1.union(set2)
print(union_set) # output: {1, 2, 3, 4, 5}
```

Alternatively, you can use the `|` operator to perform union:

```set1 = {1, 2, 3}
set2 = {3, 4, 5}
union_set = set1 | set2
print(union_set) # output: {1, 2, 3, 4, 5}
```

1. Intersection: Returns a set containing only elements that are in both sets.
```set1 = {1, 2, 3}
set2 = {3, 4, 5}
intersection_set = set1.intersection(set2)
print(intersection_set) # output: {3}
```

Alternatively, you can use the `&` operator to perform intersection:

```set1 = {1, 2, 3}
set2 = {3, 4, 5}
intersection_set = set1 & set2
print(intersection_set) # output: {3}
```
1. Difference: Returns a set containing only elements that are in the first set but not in the second set.
```set1 = {1, 2, 3}
set2 = {3, 4, 5}
difference_set = set1.difference(set2)
print(difference_set) # output: {1, 2}
```

Alternatively, you can use the `-` operator to perform difference:

```set1 = {1, 2, 3}
set2 = {3, 4, 5}
difference_set = set1 - set2
print(difference_set) # output: {1, 2}
```
1. Symmetric Difference: Returns a set containing only elements that are in either of the sets, but not in both.
```set1 = {1, 2, 3}
set2 = {3, 4, 5}
symmetric_difference_set = set1.symmetric_difference(set2)
print(symmetric_difference_set) # output: {1, 2, 4, 5}
```

Alternatively, you can use the `^` operator to perform symmetric difference:

```set1 = {1, 2, 3}
set2 = {3, 4, 5}
symmetric_difference_set = set1 ^ set2
print(symmetric_difference_set) # output: {1, 2, 4, 5}
```

### Set comparisons:

In Python, you can compare two sets using the comparison operators: `==`, `!=`, `<`, `<=`, `>`, and `>=`. Here are some examples:

1. Equality: Sets are equal if they contain the same elements, regardless of their order.
```set1 = {1, 2, 3}
set2 = {3, 2, 1}
print(set1 == set2) # output: True
```
1. Inequality: Sets are not equal if they do not contain the same elements.
```set1 = {1, 2, 3}
set2 = {3, 4, 5}
print(set1 != set2) # output: True
```
1. Subset: A set is a subset of another set if all its elements are contained in the other set.
```set1 = {1, 2}
set2 = {1, 2, 3}
print(set1.issubset(set2)) # output: True

set3 = {1, 2, 3, 4}
print(set1.issubset(set3)) # output: False
```

Alternatively, you can use the `<` operator to check if a set is a proper subset (subset but not equal):

```set1 = {1, 2}
set2 = {1, 2, 3}
print(set1 < set2) # output: True

set3 = {1, 2, 3, 4}
print(set1 < set3) # output: False
```
1. Superset: A set is a superset of another set if it contains all the elements of the other set.
```set1 = {1, 2, 3}
set2 = {1, 2}
print(set1.issuperset(set2)) # output: True

set3 = {1, 2, 3, 4}
print(set1.issuperset(set3)) # output: False
```

Alternatively, you can use the `>` operator to check if a set is a proper superset (superset but not equal):

```set1 = {1, 2, 3}
set2 = {1, 2}
print(set1 > set2) # output: True

set3 = {1, 2, 3, 4}
print(set1 > set3) # output: False
```
1. Disjoint: Two sets are disjoint if they have no common elements.
```set1 = {1, 2, 3}
set2 = {4, 5, 6}
print(set1.isdisjoint(set2)) # output: True

set3 = {2, 4, 6}
print(set1.isdisjoint(set3)) # output: False
```

### FrozenSets:

In Python, a `frozenset` is an immutable version of a `set`. Once a `frozenset` is created, its contents cannot be changed.

Here is an example of creating a `frozenset`:

```set1 = {1, 2, 3}
fset1 = frozenset(set1)
print(fset1) # output: frozenset({1, 2, 3})
```

You can perform the same set operations on `frozenset` objects as you can with `set` objects. However, you cannot modify a `frozenset` once it is created.

```fset1 = frozenset({1, 2, 3})
fset2 = frozenset({2, 3, 4})

# Union
print(fset1.union(fset2)) # output: frozenset({1, 2, 3, 4})

# Intersection
print(fset1.intersection(fset2)) # output: frozenset({2, 3})

# Difference
print(fset1.difference(fset2)) # output: frozenset({1})

# Subset
print(fset1.issubset(fset2)) # output: False

# Superset
print(fset1.issuperset(fset2)) # output: False

# Disjoint
print(fset1.isdisjoint(fset2)) # output: False
```

`frozenset` objects are useful when you need to use a set as a dictionary key, since `set` objects are not hashable (i.e., they cannot be used as dictionary keys because their contents can change).

### Frozenset for the dictionary:

You can use a `frozenset` as a key in a Python dictionary, because it is immutable and hashable, just like strings, numbers, and other immutable types.

Here’s an example of creating a dictionary that uses `frozenset` objects as keys:

```d = {frozenset({1, 2}): 'A', frozenset({2, 3}): 'B'}
print(d) # output: {frozenset({1, 2}): 'A', frozenset({2, 3}): 'B'}
```

You can access the value associated with a `frozenset` key in the same way as you would with any other key:

```d = {frozenset({1, 2}): 'A', frozenset({2, 3}): 'B'}
print(d[frozenset({1, 2})]) # output: 'A'
```

Note that you can only use immutable objects as dictionary keys, and not mutable objects like lists, sets, or dictionaries. If you try to use a mutable object as a key, you’ll get a `TypeError`:

```d = {[1, 2]: 'A'} # TypeError: unhashable type: 'list'
```

### Python Built-in set methods:

Here are some of the most commonly used built-in methods for sets in Python:

1. `set.add(item)` – Adds an item to the set if it is not already in the set.
2. `set.remove(item)` – Removes the item from the set. Raises a `KeyError` if the item is not in the set.
3. `set.discard(item)` – Removes the item from the set if it is present, otherwise does nothing.
4. `set.pop()` – Removes and returns an arbitrary item from the set. Raises a `KeyError` if the set is empty.
5. `set.clear()` – Removes all items from the set.
6. `set.copy()` – Returns a shallow copy of the set.
7. `set.union(other_set)` – Returns a new set that contains all elements from the original set and the `other_set`.
8. `set.intersection(other_set)` – Returns a new set that contains only the elements that are in both the original set and the `other_set`.
9. `set.difference(other_set)` – Returns a new set that contains the elements that are in the original set but not in the `other_set`.
10. `set.symmetric_difference(other_set)` – Returns a new set that contains the elements that are in either the original set or the `other_set`, but not in both.
11. `set.issubset(other_set)` – Returns `True` if the original set is a subset of the `other_set`, otherwise `False`.
12. `set.issuperset(other_set)` – Returns `True` if the original set is a superset of the `other_set`, otherwise `False`.
13. `set.isdisjoint(other_set)` – Returns `True` if the original set and the `other_set` have no common elements, otherwise `False`.