Unpacking over indexing

why?

  • less noisy
# do this 
a,b = somehting

# over 
a = something[0]
b = somehting[1]

another example

# don't do this
snacks = [('bacon', 350), ('donut', 240), ('muffin', 190)]
for i in range(len(snacks)):

    item = snacks[i]
    name = item[0]
    calories = item[1]
    print(f'#{i+1}: {name} has {calories} calories')

# do this
for rank, (name, calorie) in enumerate(snacks,1):
	print(rank, name, calorie)

Unpacking can be applied to any iterables (dict, lists, tuples)

Slicing

slicing creates a reference to original list, but changing sliced object won’t reflect in original list

a = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
b = a[:3]

b[0]= 10
print(b) # [10, 'b', 'c']
print(a) # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']

Striding

Avoid using striding (step) i.e start:stop:step altogether, mostly avoid them while using negative strides.

a[::-1] # outputs ['h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']
a[-2::-2] # ['g', 'e', 'c', 'a'] # its confusing, so avoid it

Starred Expression

car_ages = [0, 9, 4, 8, 7, 20, 19, 1, 6, 15]
car_ages_descending = sorted(car_ages, reverse=True)
oldest, second_oldest = car_ages_descending

# Instead of doing this 
oldest = car_ages_descending[0]
second_oldest = car_ages_descending[1]
others = car_ages_descending[2:]
print(oldest, second_oldest, others)

# Do this 
oldest, second_oldest, *others = car_ages_descending
print(oldest, second_oldest, others)

But it can only be used once in an expression, but it should alway pair with at least one normal unpacking. i.e only doing * others = car_ages_descending will not work.

Sorting

.sort() can be called on to any built-in types that has ordering, i.e int, float, str. Defaults to sorting in ascending order, use reverse=True for descending.

numbers = [93, 86, 11, 68, 70]
numbers.sort()
print(numbers) # [11, 68, 70, 86, 93]
class Tool:
     def __init__(self, name, weight):
 
         self.name = name
         self.weight = weight
    
     def __repr__(self):
         return f'Tool({self.name!r}, {self.weight})'
tools = [
    Tool('level', 3.5),
    Tool('hammer', 1.25),
    Tool('screwdriver', 0.5),
    Tool('chisel', 0.25),

]

#simply calling tools.sort() will result in error, cause Tool class doesn't implement comparision operator by default.
# sort it using key parameter.
# keys expects a function, and the should return by which we want our list to be ordered i.e by name
tools.sort(key = lambda x: x.name)

Tuples have built in order defined in them, i.e

(4, 'drill') < (4, 'sander')

in this case, 4 is compared with 4, and since its true ‘drill’ is compared with ‘sander’, we can take advantage of this tuple within our key function to defined the order i.e

tools.sort(key = lambda x: (x.weight, x.name))

In this case, it sort by ascending order, but first weight is compared AND if any two items have the same weight their name attribute is compared i.e drill and sander have same weights, so d < s


#output
[Tool('drill',        4),
 Tool('sander',       4),
 Tool('circular saw', 5),
 Tool('jackhammer',   40)]