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:

Aby potwierdzić swoją subskrypcję, odbierz pocztę i kliknij w link potwierdzający:) jeśli maila nie ma to poczekaj chwile i/lub sprawdź folder spam/inne/oferty itp :)

Aby potwierdzić swoją subskrypcję, odbierz pocztę i kliknij w link potwierdzający:) jeśli maila nie ma to poczekaj chwile i/lub sprawdź folder spam/inne/oferty itp :)
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 :)

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
Mateusz M.

Pokaż komentarze

  • 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 ;)

  • 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

    • Dzięki za komentarz, będzie więcej! A co do get() to jeśli masz pewność że w słowniku klucz będzie to oczywiście get() jest zbędne:) tu miałem na mysli sytuację że może nie być:)

  • 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 :-).

    • Oj robią takie błędy... :D dzięki! Na 100% będzie trzecia część:)

  • 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'

Ostatnie wpisy

Podsumowanie: maj, czerwiec, lipiec i sierpień 2024

Oj daaawnoo mnie tu nie było. Ale wakacje to był czas dużej liczby intensywnych wyjazdów i tak naprawdę, dopiero jakoś… Read More

2 miesiące ago

Podsumowanie: kwiecień 2024

Cześć! Zapraszam na krótkie podsumowanie kwietnia. Wyjazd do Niemiec A dokładniej pod granicę z Francją. Chrześnica miała pierwszą komunię. Po… Read More

6 miesięcy ago

Podsumowanie: luty i marzec 2024

Ostatnio tygodnie były tak bardzo wypełnione, że nie udało mi się napisać nawet krótkiego podsumowanie. Więc dziś zbiorczo podsumuję luty… Read More

7 miesięcy ago

Podsumowanie: styczeń 2024

Zapraszam na krótkie podsumowanie miesiąca. Książki W styczniu przeczytałem "Homo Deus: Historia jutra". Książka łudząco podoba do wcześniejszej książki tego… Read More

9 miesięcy ago

Podsumowanie roku 2023

Cześć! Zapraszam na podsumowanie roku 2023. Książki Zacznijmy od książek. W tym roku cel 35 książek nie został osiągnięty. Niemniej… Read More

10 miesięcy ago

Podsumowanie: grudzień 2023

Zapraszam na krótkie podsumowanie miesiąca. Książki W grudniu skończyłem czytać Mein Kampf. Nudna książka. Ciekawsze fragmenty można by było streścić… Read More

10 miesięcy ago