1. Mutable & Immutable

In python, built-in types are classified as mutable and immutable.

immutable: tuple, string, number, bool

mutable: list, dict, set

example

city_list = ['New York', 'London', 'Tokyo']
city_list.append('Paris')
print(city_list) # ['New York', 'London', 'Tokyo', 'Paris']

tuple_a = (1, 2)
tuple_a[0] = 3 # error, since tuple_a is immutable, we can't modify it

tuple_b = ([], 0)
tuple_b[0].append(1) # success, because the address of the first element didn't change
print(tuple_b) # ([1], 0)

Conlusion

Mutable objects are often used in the situations where we want to do updates. Immutable objects, on the other hand, are used in sensitive tasks where we allow for parallel processing. Since no threads can modify the values, we can ensure the data won’t change.

By the way, for integer values in closed interval [-5, 256], the address are fixed.

a = 1
b = 1
id(a) == id(b) # true
a = 257
b = 267
id(a) == id(b) # false

ordered & unordered

oredered: list, number, string, tuple

unordered: dict, set

2. Decorator

Decorators enable us to extend the function without changing it.

def test(func):
    def wrapper():
        print("start test")
        func()
        print("end test")
    return wrapper

@test
def hello():
    print("hello world")

hello()
# start test
# hello
# end test

3. Copy

Assignment: do not copy objects, but create bindings. Shallow Copy: oconstructs new compound object and insert references into it. Deep Copy: constructs new compound objects and, recursively, inserts copies into it.

import copy
a = {1: [1, 2, 3]}
b = a.copy() # shallow copy
a[1].append(4)
print(a)
# {1: [1, 2, 3, 4]}
print(b)
# {1: [1, 2, 3, 4]}

c = copy.deepcopy(a) # deep copy
a.append(5)
print(a)
# {1: [1, 2, 3, 4, 5]}
print(c)
# {1: [1, 2, 3, 4]}