8 rzeczy w Python, które musisz znać!


Poznaj 8 mechanizmów i sztuczek w Python, które musisz znać żeby twój kod będzie bardziej "pythonowy".


Każdy język programowania ma swoje zalety i wady. Na początku swojej przygody z programowaniem przechodziłem między językami, nie wnikając w sztuczki, które sprawią, że kod stanie się bardziej czytelny. To był bardzo duży błąd. Dlatego poniżej zaprezentuję Wam aż 8 porad, które bardzo chciałbym znać zanim rozpocząłem pracę zarobkową właśnie jako programista Python.

1. Enumerate

Spróbujmy napisać kod, który wypiszę listę miast z numerami porządkowymi.

Zły sposób:

countries = ['Polska', 'Niemcy', 'Grecja', 'Szwecja', 'Rosja']

i = 0
for country in countries:
    print (i, country)
    i += 1

W tym rozwiązaniu musimy mieć dodatkową zmienną, którą za każdym przejściem pętli ręcznie zwiększamy. Można to zautomatyzować:

Dobry sposób:

countries = ['Polska', 'Niemcy', 'Grecja', 'Szwecja', 'Rosja']

for i, country in enumerate(countries):
    print(i, country)

2. Zip

Mamy dwie listy. W pierwszej są kolejne liczby całkowite, a w drugiej potęgi tej liczby. Napiszmy kod, który wypisze te liczby w parach:

Zły sposób:  

x_list = [1, 2, 3, 4]
y_list = [1, 2, 4, 8]

for i in range((len(x_list)):
    x = x_list[i]
    y = y_list[i]
    print(x, y)

Zanim zaczniemy przechodzić po liście musimy sprawdzić jej długość. Następnie przechodzimy tyle razy po pętli ile jest elementów i z obu list pobieramy wartości po indeksach. Na początku mojej przygody sam tak robiłem i uważałem, że to jedyny słuszny sposób. Trochę naleciałości z pierwszych tutorialów w C ;)

Dobry sposób:

x_list = [1, 2, 3, 4]
y_list = [1, 2, 4, 8]

for x, y in zip(x_list, y_list)
    print(x, y)

Zip, to wbudowana funkcja, która pozwala łączyć dwie, lub więcej list i iterować po nich równocześnie. Kod jest bardziej kompaktowy zachowując przy tym logikę. Także polecam :)

Gdy listy są różnej długości:

import itertools

x_list = [1, 2, 3, 4]
y_list = [1, 2, 4]

for x, y in itertools.zip_longest(x_list, y_list):
    print(x, y)

Jeśli nie mamy pewności, co do długości list warto zaimportować zip_longest z itertools, który krótszą listę uzupełni domyślną wartością - None. Zdarzają się takie sytuację, więc warto znać alternatywę.

3. Podmiana wartości dwóch zmiennych

To jedno z częstych zagadnień przed, którymi stają programiści. Pierwszy raz zetknąłem się z tym podczas sortowania bąbelkowego. Wtedy napisałem to w języku C.

Zły sposób:

x = 10
y = 20

print("Przed: x = %d, y = %d" % (x, y))
tmp = y
y = x
x = tmp
print("Po: x = %d, y = %d" % (x, y))

Nie wymaga to chyba tłumaczenia. Korzystamy ze zmiennej tymczasowej, żeby przechować aktualną wartość. Ale co tu złego? Nic, gdyby nie fakt, że mówimy o Python ;)

Dobry sposób:

x = 10
y = 20

print("Przed: x = %d, y = %d" % (x, y))
x, y = y, x
print("Po: x = %d, y = %d" % (x, y))

Niesamowitaa sztuczka, podmiany wartości z prawej do lewej. Dowiedziałem się o tym stosunkowo niedawno podczas przeglądu kodu. I zauważył to młodszy programista. Dlatego przeglądajcie cudzy kod, nawet jeśli macie mniej stażu. Każdy popełnia błędy i każdy ma prawo coś zapomnieć, a taką prostą sztuczką możecie zaplusować ;)

4. Pobieranie obiektów ze słownika

Słowniki to jedna z podstawowych struktur danych, z którą przychodzi nam pracować.  Zdarza się, że nie mamy pewności, co do obecności danego klucza w słowniku. Jak zatem pobrać obiekt, bez łapania KeyError?

Zły sposób:

ages = {
    'Jan': 10,
    'Dawid': 20,
    'Marcin': 30,
}

if 'Krzysiek' in ages:
    age = ages['Krzysiek']
else:
    age = 'nieznany'

Prostę rozwiąznie, z którego sam korzystałem, do czasu aż nie poznałem prostej sztuczki poniżej

Dobry sposób:

ages = {
    'Jan': 10,
    'Dawid': 20,
    'Marcin': 30,
}

age = ages.get('Krzysiek', 'nieznany')

Takie pobieranie ze słownika pozwala pobierać wartości bez wyrzucania KeyError. Drugi parametr jest opcjonalny i jest wartoścą domyślną w przypadku braku klucza. Jeśli jej nie uzupełnimy będzie to None.

5. For .. else

Do sprawdzenia tej porady napiszmy kod, który sprawdzi czy jedna litera występuje na liście pozostałych liter

Zły sposób:

letter_to_found = 'x'
letters = ['a', 'b', 'c', 'd', 'e']

founded = False
for letter in letters:
    if letter == letter_to_found:
        print('Znaleziono')
        founded = True
        break

if not founded:
    print('Brak')

Lepszy sposób:

letter_to_found = 'x'
letters = ['a', 'b', 'c', 'd', 'e']

for letter in letters:
    if letter == letter_to_found:
        print('Znaleziono')
        break
else: # Jeśli nie wystąpił break
    print('Brak')

W tym rozwiązaniu nie korzystamy już ze zmiennej pomocniczej founded, ale dalej ręcznie sprawdzamy wszystkie elementy. Można to zrobić lepiej, na przykład przez any, ale o tym będzie inny wpis. Tu chciałem tylko pokazać, że można praktycznie wykorzystać else w pętli for.

Dobry sposób:

letter_to_found = 'x'
letters = ['a', 'b', 'c', 'd', 'e']

msg = 'Znaleziono' if letter_to_found in letters else 'Brak'
print(msg)

Po ci ręcznie iterować po wszystkich elementach, skoro jest wbudowana funkcja, która zrobi to za nas? ;)

6. Czytanie z pliku

Na każdej lekcji programowania, jaką widziałem prędzej czy później dochodzi się do obsługi plików. Niestety jeszcze nigdy nie spotkałem się z informacją dlaczego dane rozwiązanie jest lepsze bądź gorsze. Dlatego właśnie niżej przedstawię różne sposoby czytania z pliku

Zły sposób:

f = open('file.txt')
text = f.read()
for line in text.split('\n'):
    print(line)
f.close()

Otwieramy plik, do zmiennej tymczasowej wczytujemy jego zawartość, następnie przechodzimy po kolejnych liniach i je wypisujemy. Na koniec zamykamy plik. Wygląda w porządku ale po co nam zmienna tymczasowa? Może da się ten kod uprościć?

Lepszy sposób:

f = open('file.txt')
for line in f:
    print(line)
f.close()

Okazuje się, że da się to zrobić łatwiej. Nie musimy przepisywać zawartości pliku do dodatkowej zmiennej. Możemy w te miejsce bezpośrednio iterować po pliku. Dalej nie jest to rozwiązanie idealne, ale już jest znacznie lepiej.

Dobry sposób:

with open('file.txt') as f:
    for line in f:
        print(f)

To rozwiązanie jest o tyle dobre, że nie trzeba zamykać pliku a cały kod i zmienna f jest w kontekście tego pliku. Czyli już nigdy nie zapomnisz o niedomkniętych plikach, a uwierzcie, że to się zdarza. Polecam :)

Dodatkowo jeśli musisz pracować na kilku plikach jeden po drugim nie musisz wymyślać różnych nazw zmiennych. Taki bonus :)

7. Try..finaly

Co prawda o wyjątkach pisałem już w poprzednim wątku ale warto to powtórzyć, bo korzystanie z finaly daje wiele możliwości o których zbyt często się nie mówi.

Spróbujmy zrzutować na int i wypisać jakąś liczbę. Najprościej zrobić to tak jak niżej:

print(int('1'))

Jeśli zamiast 1 byłby tam napis, którego nie można zrzucić na liczbę to ten fragment wyrzuciłby wyjątek.

try:
    print(int('x'))
except ValueError:
    print('Błąd')

Jeśli czytaliście poprzedni wpis to rozumiecie zasadę działania wyjątków. Jeśli nie, zapraszam do lektury :)
Warto jednak wspomnieć o konstrukcji try..finaly

try:
    print(int('x'))
finally:
    print('Koniec')

Zauważcie, że przy takiej konstrukcji Koniec zostanie wypisany, ale wyjątek nie zostanie wyłapany, co wiąże się z przerwaniem działania aplikacji. Bardzo ciekawa konstrukcja, o której warto wiedzieć :)

8. Jednolinijkowy if

O takich konstrukcjach byłm mój pierwszy wpis na tym blogu. Nie uwzględniłem tam jednego, bardzo ciekawego przypadku. Wiemy już jak wygodne są warunki. Możemy na przykład zapisać coś w kilku linijkach jak niżej:

latest_python = 2
my_python = 2

if latest_python > my_python:
    msg = 'Aktualizacja do pobrania'
else:
    msg = 'Aktualna wersja'

Można też zapisać to w jednej linijce z zachowaniem logiki. Kod jest krótszy, ale dalej prosty do zrozumienia. Dla przypomnienia:

latest_python = 2
my_python = 2


msg = 'Aktualizacja do pobrania' if latest_python > my_python else 'Aktualna wersja'

Żeby to było bardziej realistyczne, wersja python powinna być jednak określona jako tupla:

latest_python = (2, 7, 14)
my_python = (3, 6, 3)

Jak zatem sprawdzyć czy powinniśmy zaktualizować wersję Python?

Zły sposób:

for x, y in zip(my_python, latest_python):
    if x < y:
        msg = 'Aktualizacja do pobrania'
        break
else:
    msg = 'Aktualna wersja'

Zastosowaliśmy tutaj poznane wcześniej else do fora, oraz zip. Dlaczego zatem te rozwiązanie jest zły sposób? Celowo napisałem zły sposób, a nie złe rozwiązanie, bo te samo w sobie jest w porządku. Jednak jeśli korzystamy z jakiegoś języka, to używajmy w pełni jego możliwości.

Dobry sposób:

msg = 'Aktualizacja do pobrania' if latest_python > my_python else 'Aktualna wersja'

Proste? Bardzo proste. Przyznam szczerze, że to chyba najnowsza sztuczka o której się dowiedziałem. Do niedawna porównywałem listy właśnie ręcznie. Jeśli listy są różnej długości, to porównywane są tylko wspólne indeksy. Niżej więcej przykładów.

Więcej przykładów:

def fun(a, b):
    return a < b
    
a = (1, 2, 3)
b = (1, 2, 3)
fun(a, b)  # False

a = (1, 2, 3)
b = (1, 2, 4)
fun(a, b)  # True

a = (1, 2, 3)
b = (1, 2, 3, 0)
fun(a, b)  # True

a = (1, 2, 3)
b = (1, 2, 2, 0)
fun(a, b)  # False

Podsumowanie

To jest dopiero czubek góry sztuczek i możliwości Python. Starałem się jednak przedstawić te, z którymi sam kiedyś pracowałem oraz takie, które przydały mi się w pracy i które naprawde musicie znać. Na różnych forach można znaleźć bardzo dużo innych ciekawych sztuczek ale często są to przypadki, których nigdy nie użyjecie. Oczywiście znajdą się też perełki, które oszczędzą Wam masę czasu. Spotkaliście się z takimi? A może sami znacie jakieś ciekawe? Podzielcie się nimi w komentarzu :)

Mar 27, 2019

Najnowsze wpisy

Zobacz wszystkie