ABS Python 23.03.2023

Podsumowanie spotkania:

zamiast dokańczać poprzednie tematy, wyszła spora dyskusja o widoczności zmiennych (tzw. scope albo frame), że typy są zmienialne i niezmienialne (mutable, immutable), pojawiło się pojęcie “referencja” oraz używaliśmy narzędzia do wizualizacji zmiennych/scope’ów i pamięci (Python Tutor code visualizer: Visualize code in Python, JavaScript, C, C++, and Java).

Dla powótrzenia, zaczęliśmy od takiego problemu:

x = 1
def abc():
    print(x)
    x = 2
abc()

Okazało się, że taki kod powoduje błąd. W dokumentacji (Programming FAQ — Python 3.14.3 documentation) jest taki fragment:
This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in function assigns a new value to x, the compiler recognizes it as a local variable. Consequently when the earlier print(x) attempts to print the uninitialized local variable and an error results.

Niestety akurat to narzędzie do wizualizacji nie potrafi tego ładnie pokazać, więc trzeba zaufać dokumentacji.

Drugi ciekawy problem to było tworzenie list za pomocą *, taki prosty przypadek jak poniżej jest generalnie OK (nie jestem pewny czy to nie jest jakieś bardzo niewydajne przy dużych listach).

x = [1] * 10

Problem pojawia się jak robimy jakieś dziwne rzeczy typu

x = [[]] * 10

Czyli chcemy mieć listę, która ma w sobie 10 list. I powiedzmy, że chcemy tylko do pierwszej listy dodać jakąś liczbę

x = [[]] * 10
first_list = x[0]
first_list.append(1)
print(x)

Wynik:

[[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]

Problem jest taki, że jak tworzymy listę przez mnożenie i jak elementami tej listy są inne listy, to python to zrobi tak, że będzie kopiował zawsze tę samą referencję do tej samej listy. Przez to de facto nie mamy 10 różnych list, tylko mamy 10 referencji.

To mnożenie fajnie widać w tym toolu do wizualizacji (tylko zamiast * 10 to może lepiej * 2, bo mniej strzałek jest).

Trzeci temat to:

a = [1, 2, 3]
b = a
a = 'cos innego'
print(a)
print(b)

To też polecam wrzucić do tego toola do wizualizacji.
Najpierw tworzymy listę a
potem mówimy: b ma wskazywać na ten sam obiekt w pamięci, co a
potem mówimy: a ma teraz wskazywać na string 'cos innego'
potem printujemy

I teraz widać, że b wskazuje na listę, a a na string.

Czyli jak robimy
b = a
to nie mówimy ‘b ma wskazywać ZAWSZE na to co wskazuje a
tylko mówimy ‘b ma wskazywać na to, na co w TYM momencie wskazuje a’ (ale jak a później będzie wskazywać na coś innego to b już o tym nie wiem)