6th Week Python Experience

As I delved into my sixth week of Python exploration, the journey into the intricacies of this versatile programming language took on a new dimension. With each passing week, my understanding deepens, and the horizon of possibilities expands. This week's focus was on mastering fundamental data structures such as dictionaries, tuples, and sets, unlocking their potential to streamline code, enhance efficiency, and pave the way for more complex programming endeavors. With each concept absorbed and every line of code written, my Python proficiency surges, propelling me closer to my goals as a burgeoning programmer. Join me as I recount the highlights of this enriching week, filled with discoveries, challenges, and boundless learning opportunities.

Lists and sets

  1. In Python, the range() function generates a sequence of numbers that is often used for iterating over a specific range. While range() itself doesn't create a list, you can convert its output into a list using the list() function.

Here's an example:

# Using range to generate a sequence of numbers
num_range = range(5)
print(num_range)  # Output: range(0, 5)

# Converting the range to a list
num_list = list(num_range)
print(num_list)  # Output: [0, 1, 2, 3, 4]

In this example, range(5) generates a sequence of numbers from 0 to 4 (exclusive of 5). When we convert it into a list using list(), we get a list containing those numbers.

You can also use range() with different start and end points, and even specify a step value:

# Using range with start, end, and step values
num_list = list(range(1, 10, 2))
print(num_list)  # Output: [1, 3, 5, 7, 9]

Here, range(1, 10, 2) generates a sequence starting from 1, ending at 9 (exclusive), with a step of 2. When converted into a list, it produces [1, 3, 5, 7, 9].

  1. IN operator :

    in statement to check for membership within a Lists, Tuples, Strings, Sets, Dictionaries you can indeed do that in Python.

    Here's an example:

      my_list = [1, 2, 3, 4, 5]
     if 3 in my_list:
         print("3 is in the list")
    
     my_string = "hello"
     if "e" in my_string:
         print("e is in the string")
    
     my_tuple = (1, 2, 3)
     if 4 not in my_tuple:
         print("4 is not in the tuple")
    
     my_set = {1, 2, 3}
     if 2 in my_set:
         print("2 is in the set")
    
     my_dict = {"a": 1, "b": 2, "c": 3}
     if "b" in my_dict:
         print("b is a key in the dictionary")
    
  2. Range() data type :

    In Python, the range type represents an immutable sequence of numbers. It's commonly used for looping a specific number of times in for loops and creating lists of consecutive integers efficiently.

    The syntax for the range() function is:

     range([start, stop, step])
    
    • start (optional): The starting value of the range. If omitted, it defaults to 0.

    • stop: The end value of the range. The range will include all numbers up to, but not including, this value.

    • step (optional): The step or increment between each number in the sequence. If omitted, it defaults to 1.

It's important to note that range itself is not a list, but rather a type that represents a sequence of numbers. However, it can be converted into a list using the list() constructor.

Here's an example:

    # Creating a range object
    my_range = range(5)

    # Converting the range object to a list
    my_list = list(my_range)

    print(my_list)  # Output: [0, 1, 2, 3, 4]

In this example, range(5) generates a sequence of numbers from 0 to 4. When converted into a list, it produces [0, 1, 2, 3, 4].

print(range(5)) output is like range (0,5 ) why ?

The output of range(5) being range(0, 5) might look confusing at first, but it's important to understand that this is just the representation of the range object.

When you call range(5), you're creating a range that starts from 0 and ends at 5 (exclusive), meaning it includes numbers from 0 up to, but not including, 5. The range object is displaying its start and stop values in the form range(start, stop), where start is inclusive and stop is exclusive.

So, when you see range(0, 5), it means the range includes 0, 1, 2, 3, and 4, but not 5.

This behavior is consistent with Python's convention of starting indexing at 0 and is designed to be intuitive for use in looping constructs like for loops.

# Creating a range object from 1 to 10 (exclusive)
my_range = range(1, 10)

# Converting the range object to a list
my_list = list(my_range)

print("Range:", my_range)   # Output: Range: range(1, 10)
print("List:", my_list)     # Output: List: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  1. Set datatype:

    In Python, a set is an unordered collection of unique elements. It is defined by curly braces {} enclosing a comma-separated list of elements. Sets are mutable, meaning they can be modified after creation. However, the elements themselves must be immutable (e.g., numbers, strings, or tuples containing only immutable elements).

    1. The key characteristics of sets are:

      1. Uniqueness: Sets cannot contain duplicate elements. If you try to add a duplicate element to a set, it will simply ignore the duplicate and keep only one instance of each unique element.

      2. ordered: The elements in a set are stored in a particular order. When you iterate over a set or print its contents, you notice that the order of elements do not vary each time.

      3. Mutability: Sets are mutable, meaning you can add or remove elements from them.

Here's a basic example demonstrating the definition and usage of sets in Python:

        # Creating a set
        my_set = {1, 2, 3, 4, 5}

        # Adding elements to the set
        my_set.add(6)
        my_set.add(2)  # This won't add a duplicate

        # Removing elements from the set
        my_set.remove(3)

        print(my_set)  # Output: {1, 2, 4, 5, 6}

In this example:

  • We define a set my_set containing the numbers 1 through 5.

  • We add the number 6 to the set using the add() method.

  • We attempt to add 2 again, but since it's already in the set, it won't create a duplicate.

  • We remove the element 3 from the set using the remove() method.

  • Finally, we print the contents of the set, which shows the elements in an unordered manner and without any duplicates.

Dictionaries Data Type

A dictionary in Python is a built-in data structure that stores a collection of key-value pairs. Each key in a dictionary is unique and immutable (such as strings, numbers, or tuples), and it is used to access its associated value. Dictionaries are mutable, meaning they can be modified after creation by adding, updating, or removing key-value pairs.

Key features of dictionaries include:

  1. Key-Value Pairs: Each element in a dictionary consists of a key-value pair, where the key is used to look up the associated value. Keys and values can be of any data type, and they can be mixed within the same dictionary.

  2. ordered: starting from Python 3.7, dictionaries maintain the insertion order of their keys like sequences. like lists, the elements in a dictionary are stored in any particular order. This means that the order of key-value pairs are preserved when iterating over a dictionary.

  3. Fast(Random) Access: Dictionaries offer fast access to values based on their keys. Instead of traversing the entire collection, Python's dictionary implementation uses a hash table to quickly locate the value associated with a given key.

  4. Mutable: Dictionaries can be modified after creation. You can add new key-value pairs, update existing values, or remove entries from the dictionary.

  5. Flexible and Versatile: Dictionaries are versatile data structures that can represent a wide range of relationships and data mappings. They are commonly used for tasks such as storing configuration settings, caching data, and representing structured data.

Here's a simple example of a dictionary in Python:

# Creating a dictionary
my_dict = {
    "name": "John",
    "age": 30,
    "city": "New York"
}

# Accessing values using keys
print(my_dict["name"])  # Output: John

# Adding a new key-value pair
my_dict["occupation"] = "Engineer"

# Modifying an existing value
my_dict["age"] = 35

# Removing a key-value pair
del my_dict["city"]

print(my_dict)  # Output: {'name': 'John', 'age': 35, 'occupation': 'Engineer'}

In this example, my_dict is a dictionary containing information about a person. Keys such as "name", "age", and "city" are used to access corresponding values. The dictionary is mutable, allowing for the addition, modification, and removal of key-value pairs.

An example of creating a dictionary :

In Python, you can store lists as values within dictionaries. Here's a demo illustrating how you can do this:

# Creating a dictionary with lists as values
my_dict = {
    "numbers": [1, 2, 3, 4, 5],
    "fruits": ["apple", "banana", "orange"],
    "colors": ["red", "green", "blue"]
}

# Accessing lists stored in the dictionary
print("Numbers:", my_dict["numbers"])  # Output: Numbers: [1, 2, 3, 4, 5]
print("Fruits:", my_dict["fruits"])    # Output: Fruits: ['apple', 'banana', 'orange']
print("Colors:", my_dict["colors"])    # Output: Colors: ['red', 'green', 'blue']

# Adding a new list to the dictionary
my_dict["animals"] = ["dog", "cat", "rabbit"]

# Modifying an existing list
my_dict["fruits"].append("grape")

# Removing a list from the dictionary
del my_dict["colors"]

print("\nUpdated Dictionary:")
print(my_dict)

In this demo:

  • We create a dictionary my_dict with keys "numbers", "fruits", and "colors", each containing a list as its value.

  • We access the lists stored in the dictionary using their respective keys and print their contents.

  • We add a new key-value pair to the dictionary where the value is a list of animals.

  • We modify an existing list by appending a new fruit ("grape") to the "fruits" list.

  • We remove the "colors" key along with its corresponding list from the dictionary using the del keyword.

Tuple data type

A tuple in Python is an ordered collection of elements, similar to a list. However, tuples are immutable, meaning once they are created, their elements cannot be changed, added, or removed. Tuples are defined by enclosing the elements within parentheses (), optionally separated by commas.

Key features of tuples include:

  1. Immutable: Tuples cannot be modified after creation. Once a tuple is created, its elements cannot be changed, added, or removed. This immutability provides data integrity and ensures that the tuple remains unchanged throughout its lifetime.

  2. Ordered: Like lists, tuples are ordered collections, meaning the elements are stored in a specific sequence. You can access tuple elements using their indices, just like with lists.

  3. Heterogeneous: Tuples can contain elements of different data types, similar to lists. You can mix different types of data within the same tuple.

  4. Hashable: Tuples are hashable, which means they can be used as keys in dictionaries and elements in sets. This makes tuples suitable for scenarios where immutability and hashability are required, such as creating composite keys for dictionaries or storing unique combinations of values in sets.

  5. Use Cases: Tuples are commonly used in scenarios where immutability and fixed order are desired. Some common use cases for tuples include:

    • Returning Multiple Values from Functions: Functions in Python can return multiple values as a tuple, allowing for convenient unpacking of results.

    • Immutable Data Structures: Tuples are used to represent fixed collections of data that should not be modified, such as coordinates, RGB color codes, or database records.

    • Dictionary Keys: Tuples can be used as keys in dictionaries to create composite keys that represent unique combinations of values.

    • Performance Optimization: Tuples are often preferred over lists in scenarios where data integrity and performance are critical, such as iterating over large datasets or passing arguments to functions.

While tuples share some similarities with lists and sets, they have distinct advantages in certain scenarios:

  • Priority over Lists: Tuples are preferred over lists when immutability and data integrity are required. Since tuples are immutable, they are safer to use in scenarios where you need to ensure that the data remains unchanged.

  • Priority over Sets: Tuples are hashable and can be used as elements in sets, just like immutable data types such as strings and numbers. However, tuples preserve the order of elements, which can be important in scenarios where the sequence of elements matters. Additionally, tuples can contain duplicate elements, unlike sets, which only store unique elements.

In summary, tuples are versatile data structures in Python, offering immutability, order preservation, and hashability, making them suitable for a wide range of use cases, including representing fixed collections of data, creating composite keys, and optimizing performance.

# Initiating a tuple
my_tuple = (1, 2, 3, 4, 5)

# Accessing elements of the tuple
print("First element:", my_tuple[0])  # Output: First element: 1
print("Last element:", my_tuple[-1])  # Output: Last element: 5

# Tuple unpacking
a, b, c, d, e = my_tuple
print("Unpacked elements:", a, b, c, d, e)  # Output: Unpacked elements: 1 2 3 4 5

# Length of the tuple
print("Length of tuple:", len(my_tuple))  # Output: Length of tuple: 5

# Checking if an element exists in the tuple
print("Is 3 in the tuple?", 3 in my_tuple)  # Output: Is 3 in the tuple? True
print("Is 6 in the tuple?", 6 in my_tuple)  # Output: Is 6 in the tuple? False

# Count occurrences of an element in the tuple
print("Number of occurrences of 2:", my_tuple.count(2))  # Output: Number of occurrences of 2: 1

# Finding the index of an element in the tuple
print("Index of 4:", my_tuple.index(4))  # Output: Index of 4: 3

# Creating a tuple with mixed data types
mixed_tuple = ("apple", 5, True, (1, 2, 3))
print("Mixed tuple:", mixed_tuple)  # Output: Mixed tuple: ('apple', 5, True, (1, 2, 3))

This code demonstrates various operations with tuples:

  • Initiating a tuple containing integers.

  • Accessing elements of the tuple using indices.

  • Unpacking the tuple into separate variables.

  • Finding the length of the tuple.

  • Checking for the existence of elements in the tuple.

  • Counting occurrences of a specific element.

  • Finding the index of a specific element.

  • Creating a tuple with mixed data types.

More of Lists

  1. Duplication on list :

         l1=[0]*4    #op: [0,0,0,0]
         l2=[1,2,3]*4  #op:[1,2,3,1,2,3,1,2,3,1,2,3]
         #concatenation of list [0]+[0]+[0]+[0] = [0,0,0,0]
    
  2. comparison operators between lists :

    1. Lexicographic Comparison:

      • Lexicographic comparison is a way of comparing sequences of elements based on their alphabetical or numerical order.

      • When comparing sequences like lists, tuples, or strings, lexicographic comparison involves comparing corresponding elements from left to right until a difference is found.

      • If all corresponding elements are equal, but one sequence is shorter than the other, the shorter sequence is considered less.

      • For example, when comparing strings lexically, "apple" comes before "banana" because 'a' comes before 'b' in the alphabet. Similarly, "123" comes before "456" because '1' comes before '4' in numerical order.

  1. Comparison Operations with Lists:
  • The comparison operators <, <=, >, and >= compare lists lexicographically. They start by comparing the first elements of each list. If they are equal, they move on to the next elements and continue until a difference is found. If all corresponding elements are equal and one list is shorter than the other, the shorter list is considered less.

  • For example, in list1 < list2, the first elements of list1 and list2 are compared. Since 1 is equal to 1, the comparison moves to the next elements (2 and 2). Again, they are equal, so the comparison moves to the third elements (3 and 4). Here, 3 is less than 4, so list1 is considered less than list2.

  • The == and != operators compare lists element-wise. They return True if all corresponding elements are equal and False otherwise.

    Example:

  • Consider list1 = [1, 2, 3] and list2 = [1, 2, 4].

  • For list1 < list2, the first elements (1) are equal, so the comparison moves to the second elements (2). They are also equal, so the comparison moves to the third elements (3 and 4). Since 3 is less than 4, list1 is considered less than list2.

In summary, lexicographic comparison involves comparing corresponding elements of sequences from left to right, while considering the relative order of elements. In the case of lists, the comparison operators compare lists lexicographically, while==and!=operators compare lists element-wise. If one list is shorter than the other and all its elements are equal to the corresponding elements of the longer list, it is considered less.

    # Define two lists
    list1 = [1, 2, 3]
    list2 = [1, 2, 4]
    list3 = [1, 2, 3, 4]

    # Comparison operations with lists
    print("Comparison operations with lists:")
    print("list1 < list2:", list1 < list2)  # Output: True (compares the elements lexicographically)
    print("list1 <= list2:", list1 <= list2)  # Output: True
    print("list1 > list2:", list1 > list2)  # Output: False
    print("list1 >= list2:", list1 >= list2)  # Output: False
    print("list1 == list2:", list1 == list2)  # Output: False (compares element-wise)
    print("list1 != list2:", list1 != list2)  # Output: True

    print("\nAdditional comparison:")
    print("list1 < list3:", list1 < list3)  # Output: True (compares the elements lexicographically)

some extra examples examples :

  1. Memory Location in mutable VS immutable datatypes :

  2.      x=5
         y=x
         x=10
         print(x,y) # op: 10,5
    

    In this code first of all in a particular memory location we are storing value 5 and giving a name to it "x" Then we are creating a new memory location where we are storing the 5 again and giving it a name "y" Then we are going to the memory location of X and changing it to. value to 10. So the output is 10, 5

l1=[1,2,3]
l2=l1
l1[0]=100
print(l1,l2) #op; [100,2,3]

But in python list doesn't work like integers, here python creates a memory location and stores the list [1,2,3] and gives it a name L1 And in the second line of code, it gives another name to the same location L2 , So when we go for the 3rd line and change a element in a list( L1 or L2), So it changes for both the list L1 and L2. That's why the output is [100,2,3]

how can we copy a list in a new memory location ?

3 ways :

l1= [1,2,3,4,5,6]
l2=list(l1)
l3=l1[:]
l4=l1.copy()
l5=l1
print(l1,l2,l3,l4) #op:[1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6]

how to check if more that one list point to same memory location ?

use Is operator :

l1= [1,2,3,4,5,6]
l2=list(l1)
l3=l1[:]
l4=l1.copy()
l5=l1
print(l1 is l2) # false
print(l1 is l3) # false
print(l1 is l4) #false
print(l1 is l5) # true
  1. Call by reference Vs call by value :

    Call by reference" and "call by value" are terms used to describe different mechanisms for passing arguments to functions in programming languages. Let's explore each concept:

    Call by Value:

      • In call by value, the value of the actual parameter (or argument) is copied to the formal parameter of the function.

        • The function works with a copy of the original data, so any modifications made to the formal parameter inside the function do not affect the actual parameter.

        • In call by value, primitive data types such as integers, floats, and booleans are passed by value.

        • For example, in languages like C, Java, and Python, when you pass an integer to a function, the function receives a copy of the integer value.

Example (in Python):

    def change_value(x):
        x = 10

    value = 5
    change_value(value)
    print(value)  # Output: 5 (unchanged because call by value creates a copy)

Call by Reference:

    • In call by reference, instead of passing a copy of the value, the address (reference) of the actual parameter is passed to the function.

      • The function operates directly on the original data, so any changes made to the formal parameter inside the function affect the actual parameter.

      • In call by reference, complex data types such as arrays, lists, dictionaries, and objects are passed by reference.

      • For example, in languages like C++ and JavaScript, when you pass an array to a function, the function receives a reference to the original array.

Example (in Python language):

    def change_list(lst):
        lst.append(4)

    my_list = [1, 2, 3]
    change_list(my_list)
    print(my_list)  # Output: [1, 2, 3, 4] (changed because call by reference modifies the original list)

In summary, call by value creates a copy of the data for the function to work with, while call by reference operates directly on the original data. Understanding these concepts is important for writing correct and efficient code, especially when dealing with mutable data structures and functions that modify them.

Why this happens only with mutable data ?

The reason why call by reference works only for mutable objects like lists and not for immutable objects like primitive data types (e.g., integers, floats, booleans) is related to how memory is managed in programming languages.

  1. Mutable vs. Immutable Objects:

    • Mutable objects can be modified after creation, while immutable objects cannot. Lists are mutable objects because you can change their elements, add new elements, or remove existing elements. On the other hand, primitive data types like integers are immutable because their values cannot be changed once they are assigned.
  2. Memory Management:

    • When you pass a mutable object like a list to a function, you're essentially passing a reference to the memory location where the list is stored. This means that any modifications made to the list inside the function will affect the original list because both the function and the caller are referring to the same memory location.

    • In contrast, when you pass an immutable object like an integer to a function, you're passing the value itself, not a reference to its memory location. Therefore, any modifications made to the parameter inside the function do not affect the original value because the function is working with a copy of the value, not the original value itself.

Efficiency and Safety:

  • Passing immutable objects by value ensures data integrity and prevents unintended side effects. It guarantees that the original data remains unchanged, regardless of any modifications made to the function's parameters.

  • On the other hand, passing mutable objects by reference allows for more efficient memory usage, especially when dealing with large data structures. It avoids unnecessary copying of data and allows functions to work directly with the original data, reducing memory overhead and improving performance.

In summary, call by reference works for mutable objects like lists because they can be modified in place, while call by value is used for immutable objects like primitive data types to ensure data integrity and prevent unintended side effects. This distinction allows for efficient memory management and safe manipulation of data in programming languages.

  1. List methods :

    the common methods available for lists in Python:

    1. append(x): Adds an element x to the end of the list.

    2. insert(i, x): Inserts an element x at the specified index i in the list.

    3. remove(x): Removes the first occurrence of element x from the list.

    4. pop([i]): Removes the element at the specified index i (or the last element if no index is specified) and returns it.

    5. count(x): Returns the number of occurrences of element x in the list.

    6. sort(key=None, reverse=False): Sorts the elements of the list in place. You can specify a key function to customize the sort order and use reverse=True to sort in descending order.

    7. reverse(): Reverses the elements of the list in place.

    8. index(x[, start[, end]]): Returns the index of the first occurrence of element x in the list. Optionally, you can specify a start and end index for the search.

    9. copy(): Returns a shallow copy of the list.

    10. clear(): Removes all elements from the list.

    11. extend(iterable): Extends the list by appending all elements from the iterable.

Here's an example demonstrating the usage of these methods:

    my_list = [3, 1, 4, 1, 5, 9]

    # append
    my_list.append(2)  # [3, 1, 4, 1, 5, 9, 2]

    # extend
    my_list.extend([6, 5, 3])  # [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]

    # insert
    my_list.insert(2, 8)  # [3, 1, 8, 4, 1, 5, 9, 2, 6, 5, 3]

    # remove
    my_list.remove(1)  # [3, 8, 4, 1, 5, 9, 2, 6, 5, 3]

    # pop
    my_list.pop()  # [3, 8, 4, 1, 5, 9, 2, 6, 5]

    # clear
    my_list.clear()  # []

    # count
    my_list.count(5)  # 2

    # sort
    my_list = [3, 1, 4, 1, 5, 9]
    my_list.sort()  # [1, 1, 3, 4, 5, 9]

    # reverse
    my_list.reverse()  # [9, 5, 4, 3, 1, 1]

    # copy
    new_list = my_list.copy()  # [9, 5, 4, 3, 1, 1]

These methods allow you to manipulate lists in various ways, such as adding or removing elements, sorting, and copying.

More on tuples

More on Dictionaries

d={'key':'value'} This is how we create a dictionary. every key has to be a unique value whereas duplication is allowed for values

We can use float boolean string int but can not use list,dictionary as they are mutable.

Since tuples are immutable, they can have mutable values in them. Hence, we can use Tuples as keys in a dictionary when it has immutable values. Dictionary key has to be immutable and hashable(tuples without mutable entity are allowed as dictionary keys)

What can be values ?

Values can be anything Dictionaries are the same as lists.

We can use the copy method to copy. If you want to pass a dictionary in a function it will be passed by call by reference.

d={0:0,1:1,2:4,3:9,4:16}
print(d)
for key in d:
    print( key , d[key] )

'''DICTIONARY METHODS'''
d={0:0,1:1,2:4,3:9,4:16}
print(d.keys()) #prints dict_keys([0,1,2,3,4])
print(d.values()) #prints dict_values([0,1,4,9,16])
print(d.items()) #prints dict_items([(0,0),(1,1),(2,4),(3,9),(4,16)])
#every element in the list is a tuple

More on Sets

set doesn’t allow repetitive elements.

st = {1,2,3,4,5,1,2,3,4,5}
print(st) #prints {1,2,3,4,5}

set is an unordered entity. It has no index.

Set is mutable but every value inside it has to be immutable and hashable which means you can add values into a set but those values have to be immutable. Hence, we cannot add a dictionary or list in a set.

'''SET METHODS'''
A={1,2,3}
B={1,2,3,4,5}
print(A.issubset(B)) #prints True
print(A.issuperset(B)) #prints False

A={1,2,3}
B={3,4,5}
C1=A.union(B)
C2=A | B
print(C1,C2)

A={1,2,3}
B={3,4,5}
C1=A.intersection(B)
C2=A&B
print(C1,C2)

A={1,2,3}
B={3,4,5}
C1=A.difference(B)
C2=A-B
print(C1,C2)

Remember, life is an ever-unfolding journey. Embrace each moment, cherish the memories, and keep writing your story with passion and purpose.

Btw I will be going on 10 days break as my semester exams are around.

stay safe stay healthy .