Mateusz Mazurek – programista z pasją

Python, architektura, ciekawostki ze świata IT

Programowanie Programowanie webowe

Czego nie powinniśmy robić w Pythonie? #2

Ostatnio na blogu pojawił się post „Czego nie powinniśmy robić w Pythonie? #1″ i już w trakcie jego pisania podjąłem decyzję, że wpisów z tej serii pojawi się więcej. Dziś, zgodnie z tą decyzją, pojawia się kolejny post. Pokażę w nim kolejne trzy rzeczy, których w Pythonie powinniśmy unikać.

Pomijając zbędny wstęp, zacznijmy!

Konfliktowe nazwy zmiennych

Do faktu, że poprawne nazywanie zmiennych jest niepodważalną podstawą czystego kodu nie muszę raczej nikogo przekonywać. W Pythonie powinniśmy zwrócić uwagę na to jak nazywamy zmienne, ponieważ możemy nieopatrznie nadpisać funkcje z biblioteki standardowej. Zerknij na to:

1
2
3
list = [1, 2, 3]

print(list)

Taki kod oczywiście zadziała. A teraz wyobraźmy sobie, że pod takim kodem dojdzie 1000 linii i zajdzie potrzeba otrzymania listy liczb, np tak:

1
2
3
4
5
list = [1, 2, 3]

# 1000 lines

numbers = list(range(10))

musimy tak zrobić, bo funkcja range nie zwraca listy. Szczęśliwi uruchamiamy nasz kod i… Dostajemy błąd

Traceback (most recent call last):
   File "/home/mmazurek/PycharmProjects/examples/example.py", line 5, in 
     numbers = list(range(10))
 TypeError: 'list' object is not callable


i pojawia się „wtf?!”.

Jak się pewnie domyślacie problem jest w tym, że nadpisaliśmy w pierwszej linii wbudowaną funkcję list obiektem listy, a następnie (po 1000 innych linijkach) próbujemy użyć naszej listy jako obiektu callable, a lista przecież nim nie jest. Rozwiązaniem jest zmiana nazwy zmiennej znajdującej się w pierwszej linii.

Takie błędy zdarzają się dość często. Inne funkcje, które bywają przykrywane to: type, dict, range itp.

Znalezienie takich błędów może nie być takie proste, szczególnie jeśli od deklaracji do użycia mija wiele linii kodu.


Czekaj, stop!

Podoba Ci się to co tworzę? Jeśli tak to zapraszam Cię do zapisania się na newsletter:
a w ramach prezentu otrzymasz całkowicie za darmo, dwa dokumenty PDF „6 (nie zawsze oczywistych) błędów popełnianych podczas nauki programowania” który jest jednym z efektów ponad siedmioletniej pracy i obserwacji rozwoju niejednego programisty oraz „Wstęp do testowania w Pythonie”, będący wprowadzeniem do biblioteki PyTest.
Jeśli to Cię interesuje to zapraszam również na swoje social media.

Jak i do ewentualnego postawienia mi kawy :)
Postaw mi kawę na buycoffee.to

Nieużywanie metody get

Zerknijmy na taki kod:

1
2
3
4
5
6
7
8
dictionary = {"key": "val"}

data = ""

if "key" in dictionary:
    data = dictionary["key"]

print(data)

I on sobie spokojnie zadziała. Jednak polecanym sposobem jest używanie metody .get(key, [default]) czyli np.:

1
2
3
4
5
dictionary = {"key": "val"}

data = dictionary.get("key", "")

print(data)

Zwraca nam ona wartość klucza, jeśli ten istnieje w słowniku. Jeśli taki nie istnieje, to zwraca swój drugi argument, czyli domyślną wartość. Nie dokopałem się do żadnego PEPa, który by nakazywał tę konwencję, ale jest ona zdecydowanie przejrzystsza i bardziej Python’owa. Jeśli Ty znalazłeś PEPa mówiącego o tym – podziel się w komentarzu.

Zła kolejność łapania wyjątków

Zerknij na taki kod:

1
2
3
4
5
6
7
8
9
10
def super_dangerous_func():
    raise KeyError()


try:
    super_dangerous_func()
except Exception as x:
    print("General Exception")
except KeyError as ke:
    print("KeyError")

Co on wypisze? Oczywiście że „General Exception”.

Ogólnie zasada konstruowania bloków except jest taka, żeby obsługiwać wyjątki od najbardziej szczegółowego do najbardziej ogólnego, czyli dokładnie odwrotnie jak jest na przykładzie. Powód jest prosty – prawdopodobnie, w zależności od wyjątku, będziemy chcieli inaczej na niego zareagować. Jeśli nie będziemy respektować kolejności, stracimy tę możliwość.

Oczywistym jest, że możemy łapać tylko wyjątek Exception i już w jego bloku sprawdzać, jakiej instancji jest zmienna x, ale to wypala programistom oczy.

Poprawnym przykładem byłby kod:

1
2
3
4
5
6
7
8
9
10
def super_dangerous_func():
    raise KeyError()


try:
    super_dangerous_func()
except KeyError as ke:
    print("KeyError")
except Exception as x:
    print("General Exception")

który wypisze po prostu „KeyError”.

Zmierzając do brzegu

Ło, druga cześć za nami. Trzecia się pojawi na bank, stay tuned!

Dzięki za wizytę,
Mateusz Mazurek

A może wolisz nowości na mail?

Subskrybuj
Powiadom o
guest

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.

9 komentarzy
Inline Feedbacks
View all comments
Łukasz

Bardzo fajne są takie krótkie wpisy z dużą dawką wiedzy merytorycznej. Myślę, że dzięki takim wpisom porcjujesz informacje, które łatwo można przyswoić (3 – 5 minut tygodniowo to niewiele czasu). Przykłady w kodzie są bardzo proste do zrozumienia i wydaje mi się, że byłyby jeszcze prostsze gdyby pod nimi był output z konsoli ;)

Paweł

Co do metody get to się nie zgodzę, uważam, że powinno się ją stosować tylko wtedy, gdy chcemy mieć jakąś wartość w przypadku braku elementu. Nie bez powodu są przecież kwadratowe nawiasy, jestem także zdania, że sa one bardziej przejrzyste, ale to kwestia przyzwyczajenia. Sam artykuł ciekawy i liczę na więcej :D

Mateusz Hyla

Strasznie fajny artykuł. Myślę że przesłanianie zmiennych jest tak oczywiste że nawet początkujący programiści nie popełniają tego błędu. Czy się mylę :-D? Co do wyjątków i obsługi błędów to też problem wydaje mi się bardzo błachy i nie wyobrażam sobie żeby ktoś robił tego typu błędy. Jednak fajnie że to wszystko tutaj poruszasz Mateusz. Będę wyczekiwał trzeciej części tej serii artykułów. Pozdrawiam :-).

xyz

get pozwala bezpiecznie zaglądać do słownika, w przeciwnym wypadku otrzymamy KeyErrror:

dictionary = {„a”: „b”}
print(dictionary.get(„x”))
print(dictionary.get(„x”, „y”))
print(dictionary[„x”])

None
y
Traceback (most recent call last):
 File „<string>”, line 8, in <module>
KeyError: 'x’