Nie pamiętam ile razy naśmialiśmy się w JavaScript, że zachowuje sie nieprzewidywalnie i dziwnie. Ale Python również posiada sytuacje w których kod zachowuje sie nie po naszej myśli. Niżej subiektywna lista 10 dziwnych i nietypowych zachowań w Python. Część wynika z błędów programisty a część ze specyfiki języka. Nie będę się zagłębiać i wyjaśniać dlaczego one powstają. Wykonajmy wspólnie pewnie ćwiczenie a przekonacie się, że wyniki mogą Was zaskoczyć ;)
A więc bez zbędnego przedłużania zaczynajmy. Gorąco zachęcam do zapisania rozwiązań na boku.
Atrybuty klasowe
Mamy jedną klasę bazową A i dwie klasy które z niej dziedziczą B i C. Kod przedstawiony jest niżej
class A:
a = 1
class B(A):
pass
class C(A):
pass
Jaki będzie wynik przy edycji atrybutu a ?
B.a = 10
print(A.a, B.a, C.a)
A.a = 20
print(A.a, B.a, C.a)
Wartość domyślna funkcji
Napiszmy prostą funkcję, która doda nowy element na końcu listy i wypisze całą listę.
def ddeby(blog=[]):
blog.append('Python')
print(blog)
Jaki będzie zatem wynik?
ddeby()
ddeby()
ddeby()
Scope zmiennych
Napiszmy 3 proste funkcje, które będą operować na zdefiniowanych wcześniej zmiennych. Dla urozmaicenia niech dwie operują na liście a jedna na liczbie całkowitej.
num = 5
data = ['ddeby']
def ddeby():
data.append('blog')
print(data)
def blog():
num += 5
print(num)
def dawid():
data += ['Python']
Jaki będzie wynik przy próbie wykonania tych funkcji?
ddeby()
blog()
dawid()
Metoda __str__
Zapewne każdy wie do czego służy metoda __str__. Sprawdźmy jak zachowa się przy różnych typach danych.
name = 'ddeby'
num = 5
name.__str__()
num.__str__()
'ddeby'.__str__()
5.__str__()
Inicjalizacja funkcji
W modelach danych często wykorzystuje się pole daty. Zdarza się, że zachodzi potrzeba automatycznego uzupełnienia daty. Napiszmy zatem prostą funkcję, która wyświetli aktualną datę.
from datetime import datetime
def get_today(now=datetime.today()):
return now
Jaki będzie wynik przy próbie zwrócenia tej daty?
get_today()
get_today()
get_today()
Dodawanie liczb rzeczywistych
Zaawansowana matematyka nie jest potrzebna programiście. Jednak takie podstawy jak dodawanie i odejmowanie liczb są raczej proste. Sprawdźmy czy aby na pewno. Jaki będzie wynik poniższych działań?
0.1 + 0.1
0.2 + 0.2
0.1 + 0.2
Definicja tupli
O listach i tuplach pisałem już w innym wpisie. Czy jest jeszcze jakaś różnica, której nie opisałem?
a_list = [1, 2]
b_list = [1]
a_tupe = (1, 2)
b_tuple = (1)
type(a_list)
type(b_list)
type(a_tuple)
type(b_tuple)
Warunek z in
In sprawdza czy dana wartość znajduje się na liście i zwraca wartośc logiczną. Jaki będzie zatem wynik poniższego kodu?
1 in [1]
1 in [1] is True
Operatory logiczne
Zamiast pisać wiele wcięć można sprawdzić wiele warunków za jednym razem. Operatory logiczne mają też inne ciekawe zastosowanie. Jakie wartości zostaną zwrócne w każdej z tych linijek?
False or 'python'
'ddeby' or False
'ddeby' and 'python'
False and 'python'
True and 'python'
Edycja listy
Listy są o tyle ciekawe, że znalazły się tu ponownie. Tym razem zobaczmy jak zachowa się edycja konkretnych elementów na liście. Co zostanie wypisane?
data = ['blog'] * 3
ddeby = [['blog'] * 3] * 3
data[1] = 'python'
ddeby[1][1] = 'python'
print(data)
print(ddeby)
Odpowiedzi
Atrybuty klasowe
B.a = 10
print(A.a, B.a, C.a) # 1 10 1
A.a = 20
print(A.a, B.a, C.a) # 20 10 20
Wartość domyślna funkcji
ddeby() # ['Python']
ddeby() # ['Python', 'Python']
ddeby() # ['Python', 'Python', 'Python']
Scope zmiennych
ddeby() # ['ddeby', 'blog']
blog() # UnboundLocalError: local variable 'num' referenced before assignment
dawid() # UnboundLocalError: local variable 'data' referenced before assignment
Metoda __str__
name.__str__() # 'ddeby'
num.__str__() # 5
'ddeby'.__str__() # 'ddeby'
5.__str__() # SyntaxError: invalid syntax
Inicjalizacja funkcji
get_today() # datetime.datetime(2019, 9, 23, 21, 42, 36, 67247)
get_today() # datetime.datetime(2019, 9, 23, 21, 42, 36, 67247)
get_today() # datetime.datetime(2019, 9, 23, 21, 42, 36, 67247)
Dodawanie liczb rzeczywistych
0.1 + 0.1 # 0.2
0.2 + 0.2 # 0.4
0.1 + 0.2 # 0.30000000000000004
Definicja tupli
type(a_list) # <class 'list'>
type(b_list) # <class 'list'>
type(a_tuple) # <class 'tuple'>
type(b_tuple) # <class 'int'>
Warunek z in
1 in [1] # True
1 in [1] is True # False
Operatory logiczne
False or 'python' # 'ddeby'
'ddeby' or False # 'ddeby'
'ddeby' and 'python' # 'python'
False and 'python' # False
True and 'python' # 'python'
Edycja listy
print(data) # ['blog', 'python', 'blog']
print(ddeby) # [['blog', 'python', 'blog'], ['blog', 'python', 'blog'], ['blog', 'python', 'blog']]
Bonus
Na koniec jeszcze jeden mały bonusik dla wytrwałych ;). Jak myślicie jakiego typu jest False lub True ? Sprawdźmy
type(False) is bool # True
typ(False) is int # False
Co zatem zwróci isinstance ?
isinstance(False, bool) # True
isinstance(False, int) # True
Podsumowanie
Które zachowania Was zaskoczyły? A może wszystkie rozwiązałeś poprawnie? Jeśli tak, to koniecznie podziel się tym w komentarzu. A może część z tych zagadnień wymaga dokładnego opisania w oddzielnym wpisie? Jeśli coś jest niejasne również wpisujcie to w komentarzach. Podobnie jeśli znacie inne, dziwne przypadki. Jestem bardzo ciekaw ile ciekawostek wspólnie uda nam się odszukać.