Tuples & list slicing in python

Tuples & list slicing in python

Tuples Slicing

I'm going to walk through this with an example

Write a function that adds 2 tuples.

  • Prototype: def add_tuple(tuple_a=(), tuple_b=()):

  • Returns a tuple with 2 integers:

    • The first element should be the addition of the first element of each argument

    • The second element should be the addition of the second element of each argument

  • You are not allowed to import any module

  • You can assume that the two tuples will only contain integers

  • If a tuple is smaller than 2, use the value 0 for each missing integer

  • If a tuple is bigger than 2, use only the first 2 integers

Parameters of a function are typically copies of the original values. However, when it comes to mutable objects like lists or dictionaries, changes made to them inside the function will affect the original object. This behavior is known as "pass by reference."

Immutable objects, such as tuples, strings, and numbers, cannot be modified directly. Therefore, you cannot modify the original tuples tuple_a and tuple_b directly within the function.

If you want to achieve the desired behavior of modifying the tuples, you can convert them to mutable objects, like lists, within the function, perform the modifications, and then convert them back to tuples. Here's an example:

#!/usr/bin/python3
def add_tuple(tuple_a=(), tuple_b=()):
    list_a = list(tuple_a)
    list_b = list(tuple_b)

#Your other codes goes here

    new_tuple = tuple(list_a[0] + list_b[0], list_a[1] + list_b[1])
    return new_tuple

If you want to keep the original tuples unchanged, you should create new tuples instead of modifying the existing ones.

Here's the code for a function that adds two tuples, following the given specifications:

def add_tuple(tuple_a=(), tuple_b=()):
    # Pad the tuples with zeros if they are smaller than 2
    tuple_a += (0, 0)[:2 - len(tuple_a)]
    tuple_b += (0, 0)[:2 - len(tuple_b)]

    # Calculate the sum of the elements
    sum_1 = tuple_a[0] + tuple_b[0]
    sum_2 = tuple_a[1] + tuple_b[1]

    # Return the resulting tuple
    return sum_1, sum_2

Now tuples slicing, what does this mean

tuple_a += (0, 0)[:2 - len(tuple_a)]

len(tuple_a) First how is len of a tuple calculated:

The len() function in Python returns the number of elements in a given object. When applied to a tuple, len() calculates the length of the tuple by counting the number of elements it contains.

For example, consider the following tuple:

my_tuple = (1, 2, 3, 4, 5)

To determine the length of my_tuple, you can use the len() function:

length = len(my_tuple)
print(length)  # Output: 5

In this case, len(my_tuple) returns 5 because there are five elements in the tuple.

Now Let's break down tuple slicing in a simpler manner using the expression (0, 0)[:2 - len(tuple_a)].

Tuple slicing is a way to extract a portion of a tuple by specifying the start index and end index. The general syntax is tuple[start:end], where start is the index at which the slice begins, and end is the index at which the slice ends (exclusive).

In the specific expression (0, 0)[:2 - len(tuple_a)], let's analyze it step by step:

  1. len(tuple_a) returns the length of tuple_a.

  2. 2 - len(tuple_a) calculates the difference between 2 and the length of tuple_a, representing the number of elements needed to reach a total length of 2(requirement from the question)

Now, let's break down the tuple slicing part:

  1. (0, 0)[:2 - len(tuple_a)] creates a new tuple by slicing (0, 0) using the calculated difference from step 2.

    • If tuple_a has a length of 2 or more, the difference will be 0 or negative, resulting in an empty tuple (). Because slicing something from beginning to 0(which is like the first value and means upto but including 0) will give you an empty tuple [:0]

    • If tuple_a has a length of 1, the difference will be 1, resulting in a tuple (0,).

    • If tuple_a is empty, the difference will be 2, resulting in a tuple (0, 0).

Here are a few examples to illustrate how this slicing works:

Example 1:

pythonCopy codetuple_a = (1, 2)
result = (0, 0)[:2 - len(tuple_a)]
print(result)  # Output: ()

In this case, len(tuple_a) is 2, so 2 - len(tuple_a) is 0. The slice (0, 0)[:0] results in an empty tuple ().

Example 2:

pythonCopy codetuple_a = (1,)
result = (0, 0)[:2 - len(tuple_a)]
print(result)  # Output: (0,)

Here, len(tuple_a) is 1, so 2 - len(tuple_a) is 1. The slice (0, 0)[:1] returns a tuple (0,).

Example 3:

pythonCopy codetuple_a = ()
result = (0, 0)[:2 - len(tuple_a)]
print(result)  # Output: (0, 0)

Since tuple_a is empty, len(tuple_a) is 0, and 2 - len(tuple_a) is 2. The slice (0, 0)[:2] gives the tuple (0, 0).

In summary, (0, 0)[:2 - len(tuple_a)] creates a new tuple with a length determined by the difference between 2 and the length of tuple_a. This technique is used to pad the tuple with zeros to ensure a length of at least 2 when necessary.

From the question, If a tuple is bigger than 2, use only the first 2 integers.

let's say we have a tuple with 5 items, then our formular will be this:
(0, 0)[:-3]

Let's break down this expression (0, 0)[:-3] step by step

  1. (0, 0) is the tuple on which the slicing operation is performed.

  2. :-3 is the slicing notation that specifies the range of elements to include in the slice.

In this case, :-3 indicates that we want to select elements from the start of the tuple up to the third element from the end (exclusive).

Let's evaluate the expression:

result = (0, 0)[:-3]
print(result)

Since (0, 0) has only two elements, and we are trying to slice up to the third element from the end, the resulting slice will be an empty tuple, ().

So, (0, 0)[:-3] will give you an empty tuple.

(0, 0)[:-1] if we got this instead:

  1. (0, 0) is the tuple on which the slicing operation is performed.

  2. [:-1] is the slicing notation that specifies the range of elements to include in the slice.

In this case, [:-1] indicates that we want to select all elements from the start of the tuple up to the last element (exclusive).

Let's evaluate the expression:

result = (0, 0)[:-1]
print(result)

Since (0, 0) has two elements, and we are trying to slice up to the last element, the resulting slice will include the first element of the tuple (0).

So, (0, 0)[:-1] will give you a new tuple (0,) containing only the first element of the original tuple.

Now the last part tuple_a += (0, 0)[:-1] :
To determine the final tuple after the operation tuple_a += (0, 0)[:-1], we need to consider the initial value of tuple_a and the result of (0, 0)[:-1] slicing. Let's break it down step by step:

  1. (0, 0) is a tuple with two elements, (0, 0).

  2. (0, 0)[:-1] slices the tuple to include all elements except the last one, resulting in a new tuple (0,).

Now let's consider the operation tuple_a += (0, 0)[:-1]:

tuple_a = (1, 2)
tuple_a += (0, 0)[:-1]
print(tuple_a)

Here, the initial value of tuple_a is (1, 2). The expression (0, 0)[:-1] evaluates to (0,). The += operator concatenates the tuple (0,) with tuple_a, resulting in a new tuple.

The final tuple after the operation will be (1, 2, 0). The elements from the initial tuple_a are preserved, and the element 0 from (0,) is added at the end of the tuple.

So, tuple_a += (0, 0)[:-1] modifies tuple_a by extending it with the first element from the slice (0,).

In Python, tuples are immutable, which means you cannot modify or change their existing elements. Once a tuple is created, you cannot change its elements individually.

However, you can concatenate tuples or create new tuples by combining existing tuples. When you perform operations like concatenation or slicing on a tuple, you are creating a new tuple that contains elements from the original tuple along with the added or modified elements.

For example:

tuple_a = (1, 2)
tuple_b = (3, 4)

# Concatenating two tuples to create a new tuple
new_tuple = tuple_a + tuple_b
print(new_tuple)  # Output: (1, 2, 3, 4)

# Slicing a tuple to create a new tuple
sliced_tuple = tuple_a[1:]
print(sliced_tuple)  # Output: (2,)

So, while you cannot modify or remove existing elements of a tuple, you can create new tuples by combining, slicing, or performing other operations on existing tuples.

You can also concatenate two tuples using the += operator. The += operator performs an in-place addition or concatenation operation, which means it modifies the original tuple.

Here's an example:

tuple_a = (1, 2)
tuple_b = (3, 4)

tuple_a += tuple_b
print(tuple_a)  # Output: (1, 2, 3, 4)

In this example, tuple_a is initially (1, 2). By using += with tuple_b, the elements of tuple_b are concatenated to tuple_a, resulting in a new tuple (1, 2, 3, 4). The original tuple_a is modified in-place to contain the concatenated elements.

It's important to note that the += operator specifically performs concatenation for tuples. If you try to use += with other types, such as integers or lists, it may have a different behavior or result in an error.

So, yes, you can use += to concatenate two tuples and modify the original tuple in-place.

Lists

slicing in tuple, also applies to lists

  • How to add elements to a list by squeezing them into an empty slice at the desired location:
>>> a_list = ['a', 'd', 'f']
>>> a_list[1:1] = ['b', 'c']
>>> print a_list
['a', 'b', 'c', 'd', 'f']
>>> a_list[4:4] = ['e']
>>> print a_list
['a', 'b', 'c', 'd', 'e', 'f']
  • Creating a new list without a particular element

      nums = ['A', 'B', 'C']
      # If i = 0, this creates a new list with only B and C
      nums1 = nums[:i] + nums[i+1:]
    

list of other objects

using this example:

"""This function is part of a custom Rectangle class, extracted
just to explain this concept"""
def update(self, *args):
    attr = [self.id, self.__width, self.__height, self.__x, self.__y]
    for i, v in enumerate(args):
        attr[i] = v

    self.id, self.__width, self.__height, self.__x, self.__y = attr

In Python, when you assign a value to an item in a list (attr[i] = v in this case), it updates the list in-place. However, if the list contains mutable objects (such as integers), the assignment only modifies the reference in the list and does not affect the original object outside of the list.

In the code snippet above, attr is a list that contains references to the attributes (self.id, self.__width, etc.) of the object. When you assign a new value to attr[i], it only modifies the reference in the list, but it does not update the actual attribute of the object.

Consider the following example(say you stopped at attr[i] = v:

obj = MyClass()  # Instantiate an object of MyClass
obj.update(1, 2, 3, 4, 5)
print(obj.__width)  # Output: original value of __width, not updated

In this example, even if you call obj.update(1, 2, 3, 4, 5), the __width attribute of obj will not be updated correctly because the assignment attr[i] = v only modifies the reference in the list and not the original attribute.

To properly update the attributes, you need to assign the values from attr back to the corresponding attributes of the object, as shown in the updated version of the code snippet:

self.id, self.__width, self.__height, self.__x, self.__y = attr

0x03. Python - Data Structures: Lists, Tuples
7. Tuples addition