W Pythonie kolekcja to obiekt zawierający grupę elementów i umożliwiający ich przetwarzanie jako jednej całości.
Kolekcje zwykle obsługują:
- iterację (
for element in kolekcja) - sprawdzanie przynależności (
x in kolekcja) - wyznaczanie długości (
len(kolekcja)) - dostęp przez indeks lub klucz (jeśli uporządkowane lub asocjacyjne)
Python nie ma ścisłego „interfejsu kolekcji”, ale stosuje nieformalne protokoły. Jeśli obiekt implementuje
__iter__,__len__,__contains__, można go uznać za kolekcję.
- Co NIE jest kolekcją
- Wbudowane kolekcje
- Rozszerzone kolekcje z biblioteki standardowej
- Inne typy podobne do kolekcji
- 1. Listy —
list - 2. Słowniki —
dict - 3. Krotki —
tuple - 4. SimpleNamespace
- 5. Zbiory —
set - 6. Niemutowalne zbiory —
frozenset - 7. namedtuple — krotki nazwane
- 8. deque — kolejka dwustronna
- 9. Counter — licznik elementów
- 10. defaultdict — słownik z wartościami domyślnymi
- 11. dataclass — klasy danych
- 12. UserList — niestandardowe listy
- 13. UserDict — niestandardowe słowniki
- 📈 Porównanie pamięci i wydajności
- 1. Pamięć:
listvstuplevsarray.array - 2. Szybkość dostępu:
listvstuplevsarray.array - 3. Pamięć:
dictvsSimpleNamespacevsdataclass - 4. Optymalizacja pamięci:
dataclassz__slots__ - 5. Szybkość wyszukiwania:
listvsset - 6. Pamięć:
setvsfrozenset - 7. Szybkość dodawania:
list.appendvsdeque.appendvsdeque.appendleft
- 1. Pamięć:
- 🧠 Zalecenia dotyczące wydajności
- 📊 Porównanie kolekcji
- 💡 Kiedy użyć czego?
Co NIE jest kolekcją
Następujące typy nie są kolekcjami, ponieważ nie zawierają grup elementów:
int,float,bool— wartości skalarneNone— brak wartości- funkcje, moduły, klasy — to obiekty, ale nie kontenery danych (chyba że zawierają
__dict__)
Wbudowane kolekcje
Dostępne bez importowania:
| Typ | Opis |
|---|---|
list | Uporządkowana, mutowalna sekwencja. |
tuple | Uporządkowana, niemutowalna sekwencja. |
dict | Uporządkowane (od Pythona 3.7) mapowanie klucz-wartość. |
set | Nieuporządkowana kolekcja unikalnych elementów. |
frozenset | Niemutowalna wersja set. |
Rozszerzone kolekcje z biblioteki standardowej
| Typ | Moduł | Przeznaczenie |
|---|---|---|
SimpleNamespace | types | Obiekt z dynamicznymi atrybutami (alternatywa dla dict z dostępem przez .). |
namedtuple | collections | Niemutowalna krotka z nazwanymi polami. |
deque | collections | Kolejka dwustronna — wydajna dla operacji na obu końcach. |
Counter | collections | Podklasa dict do zliczania obiektów hashowalnych. |
defaultdict | collections | Słownik z wartościami domyślnymi dla brakujących kluczy. |
dataclass | dataclasses | Automatycznie generuje __init__, __repr__, __eq__ itd. |
UserList | collections | Klasa bazowa dla niestandardowych list. |
UserDict | collections | Klasa bazowa dla niestandardowych słowników. |
Inne typy podobne do kolekcji
Choć nie zawsze nazywane „kolekcjami”, te typy również reprezentują lub przechowują grupy danych.
1. str — łańcuch znaków
Uporządkowana, niemutowalna sekwencja znaków.
s = "Python"
print(len(s)) # → 6
print(s[0]) # → P
print('y' in s) # → True
print(list(s)) # → ['P', 'y', 't', 'h', 'o', 'n']
2. bytes, bytearray
bytes— niemutowalna sekwencja bajtów.bytearray— mutowalna wersja.
b = b"czesc"
print(b[0]) # → 99
print(len(b)) # → 5
ba = bytearray(b"czesc")
ba[0] = 72
print(ba) # → bytearray(b'Hzesc')
3. range
Leniwa, uporządkowana sekwencja liczb. Nie przechowuje elementów w pamięci.
r = range(3)
print(list(r)) # → [0, 1, 2]
print(1 in r) # → True
print(r[2]) # → 2
4. array.array
Przechowuje jednorodne dane liczbowe kompaktowo (jak w C).
from array import array
arr = array('i', [1, 2, 3]) # 'i' = liczba całkowita ze znakiem
print(arr) # → array('i', [1, 2, 3])
5. Generatory i iteratory
Nie przechowują danych — generują je na żądanie. Nie obsługują len() ani indeksowania.
gen = (x * 2 for x in range(3))
print(list(gen)) # → [0, 2, 4]
# len(gen) → TypeError
6. ChainMap (z collections)
Grupuje wiele słowników w jedną widokową kolekcję — wyszukiwanie przechodzi przez mapy w kolejności.
from collections import ChainMap
d1 = {'a': 1}
d2 = {'b': 2}
cm = ChainMap(d1, d2)
print(cm['a']) # → 1
print(cm['b']) # → 2
7. OrderedDict (z collections)
Słownik, który zapamiętuje kolejność wstawiania. Istotny dla Pythona < 3.7.
from collections import OrderedDict
od = OrderedDict([('a', 1), ('b', 2)])
print(od) # → OrderedDict([('a', 1), ('b', 2)])
8. enum.Enum, enum.Flag
Zbiory nazwanych stałych.
from enum import Enum
class Kolor(Enum):
CZERWONY = 1
ZIELONY = 2
print(list(Kolor)) # → [<Kolor.CZERWONY: 1>, <Kolor.ZIELONY: 2>]
9. typing.NamedTuple, typing.TypedDict
Typowane otoczki wokół namedtuple i dict.
from typing import NamedTuple, TypedDict
class Osoba(NamedTuple):
imie: str
wiek: int
p = Osoba("Anna", 25)
class Film(TypedDict):
tytul: str
rok: int
m: Film = {"tytul": "Matrix", "rok": 1999}
10. heapq, bisect — narzędzia, nie kolekcje
Działają na kolekcjach, ale nimi nie są:
heapq— algorytm kolejki priorytetowej przez listy.bisect— utrzymuje porządek w posortowanych listach.
1. Listy — list
Uporządkowana, mutowalna kolekcja. Elementy mogą się powtarzać, dozwolone są wszystkie typy.
Używana, gdy potrzebna jest elastyczna sekwencja: dodawanie, usuwanie, modyfikowanie elementów.
Tworzenie: []
piotr_list = ["Piotr", "Warszawa", 30, "inżynier"]
print(f"Tworzenie listy: {piotr_list}")
# → Tworzenie listy: ['Piotr', 'Warszawa', 30, 'inżynier']
print(f"Element na indeksie 0: {piotr_list[0]}")
# → Element na indeksie 0: Piotr
piotr_list[2] = 31
print(f"Po zmianie: {piotr_list}")
# → Po zmianie: ['Piotr', 'Warszawa', 31, 'inżynier']
piotr_list.append("żonaty")
print(f"Po append: {piotr_list}")
# → Po append: ['Piotr', 'Warszawa', 31, 'inżynier', 'żonaty']
piotr_list.insert(1, "Polska")
print(f"Po insert: {piotr_list}")
# → Po insert: ['Piotr', 'Polska', 'Warszawa', 31, 'inżynier', 'żonaty']
piotr_list.remove("inżynier")
print(f"Po remove (wartość): {piotr_list}")
# → Po remove (wartość): ['Piotr', 'Polska', 'Warszawa', 31, 'żonaty']
del piotr_list[2]
print(f"Po usunięciu (indeks): {piotr_list}")
# → Po usunięciu (indeks): ['Piotr', 'Polska', 31, 'żonaty']
piotr_list.extend(["hobby", "wędkarstwo"])
print(f"Po extend: {piotr_list}")
# → Po extend: ['Piotr', 'Polska', 31, 'żonaty', 'hobby', 'wędkarstwo']
piotr_list.pop()
print(f"Po pop: {piotr_list}")
# → Po pop: ['Piotr', 'Polska', 31, 'żonaty', 'hobby']
2. Słowniki — dict
Kolekcja par klucz → wartość. Klucze muszą być hashowalne. Od Pythona 3.7 zachowana jest kolejność wstawiania.
Przydatne do danych strukturalnych: profile, konfiguracje, JSON.
Tworzenie: {}
anna_dict = {"imie": "Anna", "wiek": 25, "miasto": "Kraków", "zawod": "artystka"}
print(f"Tworzenie dict: {anna_dict}")
# → Tworzenie dict: {'imie': 'Anna', 'wiek': 25, 'miasto': 'Kraków', 'zawod': 'artystka'}
print(f"Wartość dla klucza 'imie': {anna_dict['imie']}")
# → Wartość dla klucza 'imie': Anna
anna_dict["wiek"] = 26
print(f"Po aktualizacji: {anna_dict}")
# → Po aktualizacji: {'imie': 'Anna', 'wiek': 26, 'miasto': 'Kraków', 'zawod': 'artystka'}
anna_dict["hobby"] = "malarstwo"
print(f"Po dodaniu pary: {anna_dict}")
# → Po dodaniu pary: {'imie': 'Anna', 'wiek': 26, 'miasto': 'Kraków', 'zawod': 'artystka', 'hobby': 'malarstwo'}
del anna_dict["miasto"]
print(f"Po usunięciu pary: {anna_dict}")
# → Po usunięciu pary: {'imie': 'Anna', 'wiek': 26, 'zawod': 'artystka', 'hobby': 'malarstwo'}
hobby = anna_dict.pop("hobby")
print(f"Po pop: {anna_dict}, wartość: {hobby}")
# → Po pop: {'imie': 'Anna', 'wiek': 26, 'zawod': 'artystka'}, wartość: malarstwo
print(f"Klucz 'imie' istnieje: {'imie' in anna_dict}")
# → Klucz 'imie' istnieje: True
3. Krotki — tuple
Uporządkowana, niemutowalna kolekcja. Nadaje się do danych stałych.
Używana, gdy niemutowalność jest ważna: współrzędne, parametry, wartości zwracane.
Tworzenie: ()
piotr_tuple = ("Piotr", "Warszawa", 30, "inżynier")
print(f"Tworzenie krotki: {piotr_tuple}")
# → Tworzenie krotki: ('Piotr', 'Warszawa', 30, 'inżynier')
print(f"Element na indeksie 2: {piotr_tuple[2]}")
# → Element na indeksie 2: 30
# piotr_tuple[0] = "Jan" → TypeError
# piotr_tuple.append("coś") → AttributeError
- Krotki zużywają mniej pamięci i są szybsze niż listy.
- Idealne, gdy nie potrzebna jest mutowalność.
4. SimpleNamespace
Prosta klasa z types do tworzenia obiektów z dynamicznymi atrybutami. Dostęp przez notację kropkową (obj.attr).
Przydatna, gdy potrzebna jest składnia obj.imie bez definiowania klasy.
from types import SimpleNamespace
anna_ns = SimpleNamespace(imie="Anna", wiek=25, miasto="Kraków")
print(f"Obiekt: {anna_ns}")
# → Obiekt: namespace(imie='Anna', wiek=25, miasto='Kraków')
print(f"Imię: {anna_ns.imie}")
# → Imię: Anna
anna_ns.wiek = 26
print(f"Po zmianie: {anna_ns}")
# → Po zmianie: namespace(imie='Anna', wiek=26, miasto='Kraków')
anna_ns.zawod = "artystka"
print(f"Z nowym atrybutem: {anna_ns}")
# → Z nowym atrybutem: namespace(imie='Anna', wiek=26, miasto='Kraków', zawod='artystka')
del anna_ns.miasto
print(f"Po usunięciu: {anna_ns}")
# → Po usunięciu: namespace(imie='Anna', wiek=26, zawod='artystka')
setattr(anna_ns, "hobby", "malarstwo")
print(f"Przez setattr: {anna_ns}")
# → Przez setattr: namespace(imie='Anna', wiek=26, zawod='artystka', hobby='malarstwo')
delattr(anna_ns, "hobby")
print(f"Przez delattr: {anna_ns}")
# → Przez delattr: namespace(imie='Anna', wiek=26, zawod='artystka')
- Alternatywa dla dict, gdy
obj.imiejest lepsze niżobj['imie'].
5. Zbiory — set
Nieuporządkowana kolekcja unikalnych elementów. Obsługuje operacje na zbiorach: suma, przecięcie, różnica.
Używana do usuwania duplikatów i sprawdzania przynależności.
Tworzenie: {} lub set()
liczby = {1, 2, 3, 3, 2, 1}
print(f"Zbiór: {liczby}")
# → Zbiór: {1, 2, 3}
liczby.add(4)
print(f"Po dodaniu: {liczby}")
# → Po dodaniu: {1, 2, 3, 4}
liczby.remove(2)
print(f"Po usunięciu: {liczby}")
# → Po usunięciu: {1, 3, 4}
inne = {3, 4, 5}
print(f"Suma: {liczby | inne}")
# → Suma: {1, 3, 4, 5}
print(f"Przecięcie: {liczby & inne}")
# → Przecięcie: {3, 4}
print(f"Różnica: {liczby - inne}")
# → Różnica: {1}
6. Niemutowalne zbiory — frozenset
Niemutowalna wersja set. Może być użyta jako klucz słownika lub element innego zbioru.
frozen = frozenset([1, 2, 3, 2])
print(f"frozenset: {frozen}")
# → frozenset: frozenset({1, 2, 3})
inne = frozenset([3, 4])
print(f"Przecięcie: {frozen & inne}")
# → Przecięcie: frozenset({3})
print(f"Suma: {frozen | inne}")
# → Suma: frozenset({1, 2, 3, 4})
# frozen.add(5) → AttributeError
7. namedtuple — krotki nazwane
Niemutowalna struktura z dostępem przez nazwę pola. Bardziej czytelna niż zwykłe krotki.
from collections import namedtuple
Osoba = namedtuple("Osoba", ["imie", "wiek", "miasto"])
anna = Osoba("Anna", 25, "Kraków")
print(f"Obiekt: {anna}")
# → Obiekt: Osoba(imie='Anna', wiek=25, miasto='Kraków')
print(f"Imię: {anna.imie}")
# → Imię: Anna
print(f"Wiek: {anna[1]}")
# → Wiek: 25
# anna.wiek = 26 → AttributeError
anna_nowe = anna._replace(wiek=26)
print(f"Kopia z zmianą: {anna_nowe}")
# → Kopia z zmianą: Osoba(imie='Anna', wiek=26, miasto='Kraków')
- Idealne do rekordów: punkty, użytkownicy, konfiguracje — gdy liczą się niemutowalność i czytelność.
8. deque — kolejka dwustronna
Zoptymalizowana pod szybkie operacje na obu końcach. Wydajniejsza niż list dla appendleft, popleft.
from collections import deque
d = deque([1, 2, 3])
print(f"Początkowy deque: {d}")
# → Początkowy deque: deque([1, 2, 3])
d.appendleft(0)
print(f"Po appendleft: {d}")
# → Po appendleft: deque([0, 1, 2, 3])
d.append(4)
print(f"Po append: {d}")
# → Po append: deque([0, 1, 2, 3, 4])
lewy = d.popleft()
print(f"Po popleft: {lewy}, pozostałe: {d}")
# → Po popleft: 0, pozostałe: deque([1, 2, 3, 4])
prawy = d.pop()
print(f"Po pop: {prawy}, pozostałe: {d}")
# → Po pop: 4, pozostałe: deque([1, 2, 3])
- Używane w algorytmach: BFS, cache LRU, bufory — gdy operacje na końcach muszą być szybkie.
9. Counter — licznik elementów
Liczy częstotliwość elementów w iterowalnym obiekcie. Przydatne do statystyk i analiz.
from collections import Counter
tekst = "abracadabra"
c = Counter(tekst)
print(f"Liczenie liter: {c}")
# → Liczenie liter: Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
print(f"Częstotliwość 'a': {c['a']}")
# → Częstotliwość 'a': 5
print(f"Top 3: {c.most_common(3)}")
# → Top 3: [('a', 5), ('b', 2), ('r', 2)]
c2 = Counter("bukva")
c.update(c2)
print(f"Po update: {c}")
# → Po update: Counter({'a': 6, 'b': 3, 'r': 2, 'c': 1, 'd': 1, 'u': 1, 'k': 1, 'v': 1})
- Przydatne do analizy tekstu, logów, głosowań — wszędzie tam, gdzie trzeba policzyć „co pojawia się najczęściej”.
10. defaultdict — słownik z wartościami domyślnymi
Automatycznie tworzy wartości domyślne dla brakujących kluczy. Eliminuje sprawdzanie if key in dict.
from collections import defaultdict
dd_list = defaultdict(list)
dd_list["owoce"].append("jabłko")
dd_list["owoce"].append("banan")
print(f"Lista: {dict(dd_list)}")
# → Lista: {'owoce': ['jabłko', 'banan']}
dd_int = defaultdict(int)
for char in "abracadabra":
dd_int[char] += 1
print(f"Liczniki: {dict(dd_int)}")
# → Liczniki: {'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}
dd_set = defaultdict(set)
dd_set["miasta"].add("Warszawa")
dd_set["miasta"].add("Kraków")
print(f"Zbiór: {dict(dd_set)}")
# → Zbiór: {'miasta': {'Warszawa', 'Kraków'}}
- Eliminuje powtarzalny kod jak
if key not in d: d[key] = []. - Czyni kod czystszy i bezpieczniejszy.
11. dataclass — klasy danych
Dekorator, który automatycznie generuje __init__, __repr__, __eq__ itd.
from dataclasses import dataclass
@dataclass
class Osoba:
imie: str
wiek: int
miasto: str = "Nieokreślone"
anna = Osoba("Anna", 25)
print(f"Obiekt: {anna}")
# → Obiekt: Osoba(imie='Anna', wiek=25, miasto='Nieokreślone')
print(f"Imię: {anna.imie}")
# → Imię: Anna
anna.wiek = 26
print(f"Po zmianie: {anna}")
# → Po zmianie: Osoba(imie='Anna', wiek=26, miasto='Nieokreślone')
bob = Osoba("Bob", 30)
print(f"Anna == Bob: {anna == bob}")
# → Anna == Bob: False
@dataclass(frozen=True)
class OsobaNiemutowalna:
imie: str
wiek: int
ivan = OsobaNiemutowalna("Ivan", 40)
# ivan.wiek = 41 → FrozenInstanceError
- Zastępuje ręczne pisanie
__init__,__repr__,__eq__. - Idealne do DTO, konfiguracji, modeli.
12. UserList — niestandardowe listy
Dziedziczy po collections.UserList. Używana do tworzenia list z niestandardowym zachowaniem.
from collections import UserList
class ListaZLogiem(UserList):
def append(self, item):
print(f"[LOG] Dodawanie: {item}")
super().append(item)
def remove(self, item):
print(f"[LOG] Usuwanie: {item}")
super().remove(item)
log_list = ListaZLogiem([1, 2, 3])
print(f"Początkowa: {log_list}")
# → Początkowa: [1, 2, 3]
log_list.append(4)
# → [LOG] Dodawanie: 4
print(f"Po append: {log_list}")
# → Po append: [1, 2, 3, 4]
log_list.remove(2)
# → [LOG] Usuwanie: 2
print(f"Po remove: {log_list}")
# → Po remove: [1, 3, 4]
- Przydatne do dodawania logowania, walidacji lub modyfikowania standardowego zachowania list.
13. UserDict — niestandardowe słowniki
Dziedziczy po collections.UserDict. Używana do tworzenia słowników z niestandardowym zachowaniem.
from collections import UserDict
class SlownikMaleLitery(UserDict):
def __setitem__(self, key, value):
key = key.lower() if isinstance(key, str) else key
super().__setitem__(key, value)
def __getitem__(self, key):
key = key.lower() if isinstance(key, str) else key
return super().__getitem__(key)
ld = SlownikMaleLitery()
ld["Imie"] = "Anna"
print(f"Wartość dla 'Imie': {ld['Imie']}")
# → Wartość dla 'Imie': Anna
print(f"Wartość dla 'imie': {ld['imie']}")
# → Wartość dla 'imie': Anna
print(f"Klucze: {list(ld.keys())}")
# → Klucze: ['imie']
- Używana do normalizacji kluczy, walidacji, logowania, cache’owania itp.
📈 Porównanie pamięci i wydajności
Wybór kolekcji wpływa na wydajność i zużycie pamięci. Poniżej praktyczne benchmarki.
1. Pamięć: list vs tuple vs array.array
import sys
from array import array
n = 1_000_000
data = list(range(n))
data_t = tuple(range(n))
data_a = array('i', range(n))
print(f"list: {sys.getsizeof(data) / 1024 / 1024:.2f} MB")
# → list: 8.00 MB
print(f"tuple: {sys.getsizeof(data_t) / 1024 / 1024:.2f} MB")
# → tuple: 8.00 MB
print(f"array: {sys.getsizeof(data_a) / 1024 / 1024:.2f} MB")
# → array: 3.81 MB
array.arrayzużywa ~2x mniej pamięci dla liczb.listituplezużywają podobną pamięć, aletuplejest nieco szybszy przy iteracji.
2. Szybkość dostępu: list vs tuple vs array.array
import time
def time_access(collection, name):
start = time.perf_counter()
total = 0
for i in range(len(collection)):
total += collection[i]
end = time.perf_counter()
print(f"{name}: {end - start:.4f} sekund")
n = 10_000_000
lst = list(range(n))
tpl = tuple(range(n))
arr = array('i', range(n))
time_access(lst, "list") # → list: 1.2000 sekund
time_access(tpl, "tuple") # → tuple: 1.0000 sekund
time_access(arr, "array") # → array: 0.8000 sekund
array.arrayjest najszybszy dla danych liczbowych.tuplejest 10–20% szybszy niżlist.- Różnica zauważalna przy dużych zbiorach.
3. Pamięć: dict vs SimpleNamespace vs dataclass
d = {"imie": "A", "wiek": 25, "miasto": "X", "hobby": "Y", "praca": "Z"}
ns = SimpleNamespace(imie="A", wiek=25, miasto="X", hobby="Y", praca="Z")
dc = OsobaDC("A", 25, "X", "Y", "Z")
print(f"dict: {sys.getsizeof(d)} bajtów") # → 232
print(f"SimpleNamespace: {sys.getsizeof(ns)} bajtów") # → 64
print(f"dataclass: {sys.getsizeof(dc)} bajtów") # → 64
print(f"ns.__dict__: {sys.getsizeof(ns.__dict__)} bajtów") # → 232
SimpleNamespaceidataclasszużywają tyle samo pamięci codictz powodu__dict__.- Użyj
__slots__do oszczędzania pamięci.
4. Optymalizacja pamięci: dataclass z __slots__
@dataclass
class OsobaSlots:
__slots__ = ("imie", "wiek", "miasto", "hobby", "praca")
imie: str
wiek: int
miasto: str
hobby: str
praca: str
dc_slots = OsobaSlots("A", 25, "X", "Y", "Z")
print(f"dataclass + slots: {sys.getsizeof(dc_slots)} bajtów")
# → 80 bajtów
# dc_slots.nowe = "wartość" → AttributeError
__slots__oszczędza pamięć i przyspiesza dostęp do atrybutów.- Wada: nie można dodawać atrybutów dynamicznie.
5. Szybkość wyszukiwania: list vs set
n = 1_000_000
lst = list(range(n))
st = set(range(n))
def time_in(collection, target, name):
start = time.perf_counter()
for _ in range(1000):
_ = target in collection
end = time.perf_counter()
print(f"{name} (szukanie {target}): {end - start:.4f} sekund")
time_in(lst, 999_999, "list") # → 10.0000 sekund
time_in(st, 999_999, "set") # → 0.0005 sekund
setjest tysiące razy szybszy niżlistprzy sprawdzaniu przynależności.- Zawsze używaj
setdo częstych sprawdzeńx in collection.
6. Pamięć: set vs frozenset
s = set(range(1000))
fs = frozenset(range(1000))
print(f"set: {sys.getsizeof(s)} bajtów") # → 32792
print(f"frozenset: {sys.getsizeof(fs)} bajtów") # → 32792
frozensetisetzużywają identyczną pamięć.- Różnica tylko w mutowalności.
7. Szybkość dodawania: list.append vs deque.append vs deque.appendleft
from collections import deque
import time
def time_append(collection, n, method='append'):
start = time.perf_counter()
for i in range(n):
if method == 'appendleft' and hasattr(collection, 'appendleft'):
collection.appendleft(i)
else:
collection.append(i)
end = time.perf_counter()
return end - start
n = 100_000
lst = []
dq = deque()
time_list_append = time_append(lst, n) # → 0.0100 sekund
time_deque_append = time_append(dq, n) # → 0.0100 sekund
time_deque_appendleft = time_append(deque(), n, 'appendleft') # → 0.0100 sekund
# list.insert(0):
lst = []
start = time.perf_counter()
for i in range(n):
lst.insert(0, i)
end = time.perf_counter()
print(f"list.insert(0): {end - start:.4f} sekund") # → 5.0000 sekund
deque.appendleftdziała w O(1), w przeciwieństwie dolist.insert(0), które jest O(n).- Używaj
dequedo częstych operacji na obu końcach.
🧠 Zalecenia dotyczące wydajności
| Sytuacja | Użyj | Powód |
|---|---|---|
| Przechowywanie liczb, pamięć krytyczna | array.array | 2x mniej pamięci, szybszy dostęp |
| Dane niemutowalne | tuple | Szybszy niż list, bezpieczniejszy |
Częste sprawdzanie x in collection | set / frozenset | O(1) vs O(n) dla list |
| Operacje na obu końcach | deque | appendleft/popleft w O(1) |
| Dane strukturalne, pamięć krytyczna | dataclass + __slots__ | Brak __dict__, mniej pamięci |
| Zliczanie częstotliwości | Counter | Zoptymalizowane pod to zadanie |
| Niestandardowe zachowanie | UserList / UserDict | Bezpieczne rozszerzenie wbudowanych kolekcji |
📊 Porównanie kolekcji
| Typ | Uporządkowany | Mutowalny | Unikalne elementy | Dostęp przez indeks | Duplikaty |
|---|---|---|---|---|---|
list | ✅ Tak | ✅ Tak | ❌ Nie | ✅ Tak | ✅ Tak |
tuple | ✅ Tak | ❌ Nie | ❌ Nie | ✅ Tak | ✅ Tak |
dict | ✅ Tak* | ✅ Tak | Tylko klucze | ❌ Nie | Wartości: ✅ |
set | ❌ Nie | ✅ Tak | ✅ Tak | ❌ Nie | ❌ Nie |
frozenset | ❌ Nie | ❌ Nie | ✅ Tak | ❌ Nie | ❌ Nie |
SimpleNamespace | ✅ Tak (atrybuty) | ✅ Tak | ❌ Nie (atrybuty mogą się powtarzać semantycznie) | ❌ Nie | ✅ Tak |
namedtuple | ✅ Tak | ❌ Nie | ❌ Nie | ✅ Tak | ✅ Tak |
deque | ✅ Tak | ✅ Tak | ❌ Nie | ✅ Tak | ✅ Tak |
Counter | ❌ Nie | ✅ Tak | ❌ Nie | ❌ Nie (ale ma klucze) | ✅ Tak |
defaultdict | ✅ Tak* | ✅ Tak | Tylko klucze | ❌ Nie | Wartości: ✅ |
dataclass | ✅ Tak (pola) | ✅ Tak (jeśli nie frozen) | ❌ Nie | ❌ Nie | ✅ Tak |
UserList | ✅ Tak | ✅ Tak | ❌ Nie | ✅ Tak | ✅ Tak |
UserDict | ✅ Tak* | ✅ Tak | Tylko klucze | ❌ Nie | Wartości: ✅ |
str | ✅ Tak | ❌ Nie | ❌ Nie | ✅ Tak | ✅ Tak |
bytes | ✅ Tak | ❌ Nie | ❌ Nie | ✅ Tak | ✅ Tak |
bytearray | ✅ Tak | ✅ Tak | ❌ Nie | ✅ Tak | ✅ Tak |
range | ✅ Tak | ❌ Nie | ❌ Nie | ✅ Tak | ❌ Nie |
array.array | ✅ Tak | ✅ Tak | ❌ Nie | ✅ Tak | ✅ Tak |
ChainMap | ✅ Tak* | ✅ Tak | Tylko klucze | ❌ Nie | Wartości: ✅ |
Enum | ✅ Tak | ❌ Nie | ✅ Tak (elementy) | ❌ Nie | ❌ Nie |
- — od Pythona 3.7,
dict,defaultdict,UserDict,ChainMapzachowują kolejność wstawiania.
💡 Kiedy użyć czego?
| Zadanie | Kolekcja |
|---|---|
| Mutowalna sekwencja | list / deque / UserList / bytearray |
| Dane niemutowalne | tuple / namedtuple / frozenset / str / bytes |
| Szybkie wyszukiwanie, usuwanie duplikatów | set / frozenset |
| Dane strukturalne | dict / dataclass / SimpleNamespace / UserDict / TypedDict |
| Używalne jako klucz słownika | frozenset |
Tymczasowe obiekty z dostępem przez . | SimpleNamespace / dataclass |
| Zliczanie częstotliwości | Counter |
| Wartości domyślne dla kluczy | defaultdict |
| Wydajne operacje na końcach | deque |
| Niestandardowe zachowanie listy | UserList |
| Niestandardowe zachowanie słownika | UserDict |
| Dane binarne | bytes / bytearray / array.array |
| Konfiguracje z hierarchią | ChainMap |
| Nazwane stałe | Enum |
| Leniwe sekwencje | range / generatory |