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

Witam w trzecim artykule z serii dotyczącej tego, czego nie powinniśmy robić w Pythonie. Załączam dwa poprzednie wpisy:

i bez przydługiego wstępu, przejdźmy do treści.

Z rozwagą używajmy metody insert z obiektu listy

Metoda insert(index, element) pozwala dodać do listy element na konkretnym miejscu (indeksie). Zerknijmy na kod:

1
2
3
4
5
6
numbers = []

numbers.insert(0, 'jeden')
numbers.insert(len(numbers), 'dwa')

print(numbers)

Ponieważ najpierw dodaliśmy do listy na indeksie zerowym słowo „jeden”, a potem na indeksie równym długości listy (a więc tu na pierwszym) słowo „dwa”, wobec tego uzyskany wynik to:

['jeden', 'dwa']

I to wszystko jest pewnie jasne.

To przed czym chciałbym przestrzec, to sytuacja w której dodajemy metodą insert jakiś element na zerowy indeks, ponieważ Python będzie zmuszony do przesunięcia reszty zawartości tej listy.

1
2
3
4
5
6
numbers = []

numbers.insert(0, 'jeden')
numbers.insert(0, 'dwa')

print(numbers)

wynik wykonania się tego kodu to

['dwa', 'jeden']

Czemu w takim razie nie powinno się używać tej metody do dodawania elementów na początek listy? Bo to jest bardzo nieefektywne rozwiązanie.

Jeśli piszesz swój kod i już wiesz, że będziesz potrzebował dodawać elementy na początek listy, radzę skorzystać z obiektu deque. Wykorzystajmy timeit’a i zmierzmy czas wykonania się bliźniaczych operacji dla listy i dla deque (double-ended queue czyli lista dwukierunkowa):

1
2
3
4
from timeit import timeit

print(timeit('s.appendleft(37)', 'import collections; s = collections.deque()', number=100000))
print(timeit('s.insert(0,37)', 's = []', number=100000))

wykonanie takiego kodu da rezultat:

0.007789244002196938 # dla deque
2.211549069004832 # dla listy

Różnica wynika ze złożoności obliczeniowej obu rozwiązań, a złożoność obliczeniowa ze sposobu implementacji ich w Pythonie. Złożoność obliczeniowa dla list.insert() jest liniowa, a dla deque.appendleft() jest stała, czyli niezależna od ilości elementów.


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

Brak rozpakowywania list

Czasem potrzebujemy przypisać elementy z listy do zmiennych, by móc ich potem używać. Możemy to zrobić tak:

1
2
3
4
5
6
7
some_values = ['jeden', 2, 'three']

first = some_values[0]
second = some_values[1]
third = some_values[2]

print(first, second, third)

co oczywiście – zadziała, ale jest to bardzo mało Pythonowe rozwiązanie. Możesz zrobić to lepiej w taki sposób:

1
2
3
4
5
some_values = ['jeden', 2, 'three']

first, second, third = some_values

print(first, second, third)

Czemu tak? Bo to ładne i przede wszystkim Python’owe rozwiązanie. Wynik obu rozwiązań jest taki sam!

Używanie listy do kolekcji elementów w których będziemy tylko szukać

Najlepiej sytuację o której myślę pokaże kawałek kodu:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Shop:
  STATES = ['open', 'closed', 'temporary_closed', 'nearly_closed']

  def __init__(self, state):
      self.state = state

  def has_valid_state(self):
      return self.state in self.STATES


s = Shop('open')

print(s.has_valid_state())

Klasa sklepu ma swoje możliwe stany i w oparciu o wartość pola zwracamy informację, czy aktualny stan jest poprawny. Na 100% widziałeś podobny kod wiele razy.

To podejście oczywiście spełni swoją funkcję. Jest jednak nieoptymalne.

Jeśli chcemy tworzyć kolekcję, której jedynym zastosowaniem jest sprawdzanie, czy jakiś element w niej istnieje, np jak w klasie wyżej – do zawężenia możliwych stanów elementu, powinniśmy zrezygnować z listy. I użyć zbioru.

I znów, udowodnijmy to korzystając z timeit’a!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from timeit import timeit


setup1 = """
STATES = {"OPEN", "NEARLY_CLOSED", "NEARLY_OPEN", "TEMPORARY_CLOSED", "CLOSED"}
"""


setup2 = """
STATES = ["OPEN", "NEARLY_CLOSED", "NEARLY_OPEN", "TEMPORARY_CLOSED", "CLOSED"]
"""



print("Checking in set with not exists element: ", timeit("'NO_EXISTS' in STATES", setup=setup1, number=100000))
print("Checking in list with not exists element: ", timeit("'NO_EXISTS' in STATES", setup=setup2, number=100000))


print("Checking in set with exists element: ", timeit("'NEARLY_OPEN' in STATES", setup=setup1, number=100000))
print("Checking in list with exists element: ", timeit("'NEARLY_OPEN' in STATES", setup=setup2, number=100000))

i wynik kodu:

Checking in set with not exists element: 0.0025714500006870367
Checking in list with not exists element: 0.006017967999468965
Checking in set with exists element: 0.0023319259998970665
Checking in list with exists element: 0.0035892980004064157

Sprawdzamy tu 4 przypadki:

  • szukanie w zbiorze nie istniejącego elementu
  • szukanie w liście nie istniejącego elementu
  • szukanie w zbiorze istniejącego elementu
  • szukanie w liście istniejącego elementu

I, co widać po czasach, wygrywa zbiór. Zatem polecam używać go w sytuacjach kiedy rozważamy posiadanie kolekcji w której będziemy tylko szukać.

Tym wnioskiem kończymy ten wpis. Będą kolejne :)

Dzięki za wizytę,
Mateusz Mazurek
Mateusz M.

Pokaż komentarze

  • Trochę już w Pythonie programuję, ale dzięki tej serii widzę co mogę robić lepiej :)

  • Czy korzystanie z seta zamiast listy wygrywa tylko czas sprawdzania i to jeszcze nie rzędem a jakimś mniejszym przelicznikiem?

    Jak dla mnie nie jest to argument za używaniem set'a w tym przypadku. Jeśli używamy pythona i myślimy tylko o czasie wykonywania kodu w ms... to używamy złego języka w mojej skromnej opinii :) Bardziej bym się zastanawiał wtedy czy nie używać enuma i sprawdzania typów na wejściu aniżeli bawił się w takie optymalizację kodu

    • Heloł! Myślę że tak proste optymalizacje jak ta to raczej kwestia bardziej czystego kodu i świadomości niż myślenia tylko o czasie wykonywania kodu:) ja bym chyba nawet nie używał słowa "optymalizacja" a bardziej "dobra praktyka". To trochę jak wbijanie gwoździa - możesz to zrobić śrubokrętem ale wygodniej będzie młotkiem:)

    • To jest elementarne dobieranie właściwej struktury danych. Optymalizacja prędkości wykonania jest jedynie produktem ubocznym i zakładam, że autor użył jej jako przykładu jednej z wielu korzyści płynących z dobrych praktyk programistycznych.

  • Rozpakowywanie listy można też wykonać w następujący sposób (przydatne np. do przechowywania kompaktowo wartości stałych):

    some_values = [1, 2, 3]
    
    def sum_of_three(int1, int2, int3):
      return int1 + int2 + int3
    
    
    print(sum_of_three(*some_values))
    

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