Ana içeriğe geç
Kesit Python
Orta · Standart kütüphane

Standart kütüphane turu

pathlib, shutil, argparse, decimal, statistics, hashlib ve secrets gibi "pilleri dahil" modüllerle dosya, komut satırı, para hesabı ve güvenlik işlerini tek satır import ile çözersin.

Uygulamada aç, 21 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

Neden "piller dahil"?

Python'ın sloganlarından biri "batteries included" (piller dahil): kurulumla birlikte gelen standart kütüphane (standard library) yüzlerce modül içerir. Anadolu Market'te bir analist olarak her gün şunlarla karşılaşırsın: rapor klasörünü taramak, betiğe komut satırından parametre vermek, kuruşu kuruşuna tutar hesaplamak, satış rakamlarının ortalamasını ve sapmasını almak, bir parolayı güvenli saklamak. Bunların hiçbiri için pip ile paket kurman gerekmez. Bu ders, en sık kullanacağın modülleri tek turda gezer; her birini derinlemesine değil, "ne zaman hangisine uzanırım" düzeyinde öğrenirsin.

İhtiyaçModülAnahtar fonksiyon / sınıf
Dosya yolu, klasör, okuma-yazmapathlib, os, shutil, tempfilePath, mkdir, glob, copy, rmtree
Komut satırı argümanıargparse, sysArgumentParser, parse_args, sys.argv
Zaman ölçme, beklemetimeperf_counter, sleep
Rastgelelikrandom, secretsseed, choice, sample / token_hex
İstatistik ve matematikstatistics, mathmean, median, stdev / ceil, floor, sqrt
Metin sarmatextwrapfill, shorten, dedent
Sabit kümeenumEnum, auto
Kesin ondalık, kesirdecimal, fractionsDecimal, quantize / Fraction
Özet (hash)hashlibsha256, hexdigest

Dosya sistemi: pathlib, shutil, tempfile

pathlib.Path, dosya yolunu bir nesne olarak temsil eder; / işleciyle parça ekler, .name, .suffix, .parent ile parçalara ayırırsın. os modülü daha eski ve düşük seviyelidir; os.environ ve os.getcwd() gibi birkaç şey için hâlâ gerekir. shutil ise "kabuk yardımcıları": kopyalama, taşıma, klasörü komple silme. Aşağıdaki örnek, geçici bir klasörde (tempfile) küçük bir rapor arşivi kurar; bu klasör bellekte oluşur ve with bloğu bitince otomatik silinir.

import shutil
import tempfile
from pathlib import Path

with tempfile.TemporaryDirectory() as gecici:
    kok = Path(gecici) / "raporlar"
    kok.mkdir()
    for ay in ("ocak", "subat", "mart"):
        (kok / f"satis_{ay}.csv").write_text(f"sehir,tutar\nAnkara,{len(ay) * 100}\n", encoding="utf-8")
    (kok / "notlar.txt").write_text("Mart raporu eksik kontrol edilecek", encoding="utf-8")

    csvler = sorted(p.name for p in kok.glob("*.csv"))
    print("CSV dosyaları:", csvler)
    print("İlk dosyanın gövdesi:", (kok / "satis_ocak.csv").stem, "| uzantı:", (kok / "satis_ocak.csv").suffix)

    yedek = Path(gecici) / "yedek"
    shutil.copytree(kok, yedek)
    print("Yedekteki dosya sayısı:", len(list(yedek.iterdir())))

    shutil.rmtree(yedek)
    print("Yedek silindi mi?", not yedek.exists())
print("Geçici klasör de silindi mi?", not Path(gecici).exists())

glob("*.csv") deseni yalnızca o klasöre bakar; alt klasörler dahil aramak için rglob kullan. write_text / read_text dosyayı açıp kapatma işini senin yerine yapar.

Yol birleştirmede string toplama yapmaklasor + "/" + ad yerine Path(klasor) / ad yaz. Windows'ta ters eğik çizgi, Linux'ta düz çizgi sorununu pathlib kendisi çözer.

Komut satırı: sys.argv ve argparse

Betiği terminalden python rapor.py --sehir Ankara --limit 5 diye çalıştırdığında argümanlar sys.argv listesine düşer. Bunları elle ayrıştırmak yerine argparse kullanırsın: tür dönüşümü, varsayılan değer, zorunluluk kontrolü ve --help çıktısı hazır gelir. Testte ya da burada tarayıcıda gerçek terminal olmadığı için parse_args'a listeyi elinle verirsin; bu, argparse'ı test etmenin de standart yoludur.

import argparse

def parser_kur() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(prog="rapor", description="Anadolu Market şehir raporu")
    p.add_argument("--sehir", required=True, help="Raporlanacak şehir")
    p.add_argument("--limit", type=int, default=10, help="Kaç satır gösterilsin")
    p.add_argument("--ozet", action="store_true", help="Yalnızca toplamları yaz")
    return p

p = parser_kur()
a = p.parse_args(["--sehir", "Ankara", "--limit", "5"])
print(a.sehir, a.limit, a.ozet)
b = p.parse_args(["--sehir", "İzmir", "--ozet"])
print(b.sehir, b.limit, b.ozet)
print(type(b.limit).__name__)

action="store_true" bayrak (flag) tanımlar: yazılırsa True, yazılmazsa False. type=int sayesinde "5" metni tam sayıya çevrilir; geçersiz bir değer gelirse argparse anlaşılır bir hata basıp çıkar.

sys ne işe yarar?sys.argv dışında sys.exit(1) ile betiği hata koduyla bitirir, sys.version ile sürümü okur, sys.path ile modül arama yollarını görürsün. Betiğin "başarısız" bittiğini zamanlayıcıya bildirmenin yolu sıfır dışı bir çıkış kodudur.

Para hesabı: neden float değil, Decimal?

Ilgaz Bank'ta 0.1 + 0.2 liranın 0.3 çıkmaması kabul edilemez. Float ikili tabanda saklandığı için bazı ondalıkları tam temsil edemez. decimal.Decimal ise onluk tabanda, senin belirlediğin hassasiyette çalışır. İki kural: Decimal'i string'den kur (float'tan kurarsan hata taşınır) ve sonucu quantize ile kuruşa yuvarla.

from decimal import Decimal, ROUND_HALF_UP
from fractions import Fraction

print("float:  ", 0.1 + 0.2)
print("Decimal:", Decimal("0.1") + Decimal("0.2"))

tutar = Decimal("1234.567")
kdv = tutar * Decimal("0.20")
print("KDV ham:", kdv)
print("KDV kuruş:", kdv.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))

# Yanlış kullanım: float'tan Decimal
print("float'tan:", Decimal(0.1))

# Kesir: 3 ortağın payı tam olarak 1/3
pay = Fraction(1, 3)
print("3 pay toplamı:", pay * 3, "| ondalık:", float(pay))

ROUND_HALF_UP ticari yuvarlamadır (0.005 yukarı). Varsayılan ROUND_HALF_EVEN bankacı yuvarlaması olarak bilinir; muhasebe kuralına göre hangisini kullanacağını iş birimine sor. Fraction ise oran ve pay hesaplarında kayıpsız sonuç verir.

Decimal ile float'ı karıştırmaDecimal("10") * 0.2 yazarsan TypeError alırsın. Çarpanı da Decimal yap: Decimal("10") * Decimal("0.2"). Tam sayıyla çarpmak serbesttir.

Sayılar ve rastgelelik: statistics, math, random, time

Küçük bir satış listesinin özetini çıkarmak için pandas'a gerek yok. statistics ortalama, medyan ve standart sapmayı verir; math yuvarlama ve kök işlemlerini; random örneklem seçmeyi; time.perf_counter ise bir işin kaç saniye sürdüğünü ölçmeyi sağlar.

import math
import random
import statistics
import time

satislar = [1250, 980, 1430, 1120, 2990, 1010, 1180]
print("ortalama:", round(statistics.mean(satislar), 2))
print("medyan:  ", statistics.median(satislar))
print("sapma:   ", round(statistics.stdev(satislar), 2))
print("en büyük tam sayı sayfa:", math.ceil(len(satislar) / 3), "| kök:", math.sqrt(144))

random.seed(42)
print("denetim için seçilen 3 satış:", random.sample(satislar, 3))

t0 = time.perf_counter()
toplam = sum(i * i for i in range(100_000))
print("süre (sn) sıfırdan büyük mü?", time.perf_counter() - t0 > 0)

random.seed(42) deterministik sonuç verir: aynı tohumla aynı örneklem çıkar. Test yazarken ve raporu tekrarlanabilir kılarken bu şarttır. Medyanın ortalamadan küçük olması, 2990 gibi bir uç değerin ortalamayı yukarı çektiğini anlatır.

Metin, sabit küme, özet: textwrap, enum, hashlib, secrets

Son grup birbirinden bağımsız ama hepsi günlük işte çıkar. textwrap.fill uzun açıklamayı satırlara böler, shorten listede sığmayan başlığı kısaltır. enum.Enum, "ONAYLI / BEKLEMEDE / RED" gibi sabit kümeleri yazım hatasına kapalı hale getirir. hashlib bir metnin parmak izini (hash) üretir; parola doğrulamada parolayı değil, tuzla (salt) birleştirilmiş özetini saklarsın. Tuz üretmek için random değil, kriptografik secrets modülü kullanılır.

import hashlib
import secrets
import textwrap
from enum import Enum, auto

aciklama = "Anadolu Market, müşterinin iade talebini 14 gün içinde değerlendirir ve sonucu e-posta ile bildirir."
print(textwrap.fill(aciklama, width=40))
print(textwrap.shorten(aciklama, width=30, placeholder="..."))

class Durum(Enum):
    BEKLEMEDE = auto()
    ONAYLI = auto()
    RED = auto()

basvuru = Durum.ONAYLI
print(basvuru, "|", basvuru.name, "|", basvuru.value, "|", basvuru is Durum["ONAYLI"])

tuz = secrets.token_hex(8)
parola = "Gizli123"
ozet = hashlib.sha256((tuz + parola).encode("utf-8")).hexdigest()
print("tuz uzunluğu:", len(tuz), "| özet uzunluğu:", len(ozet))
print("aynı parola aynı özeti verir mi?", ozet == hashlib.sha256((tuz + parola).encode("utf-8")).hexdigest())
print("parola değişince özet değişir mi?", ozet != hashlib.sha256((tuz + "gizli123").encode("utf-8")).hexdigest())

Özet tek yönlüdür: özetten parolaya dönülemez. Tuz, iki müşteri aynı parolayı seçse bile saklanan özetlerin farklı olmasını sağlar. Gerçek sistemlerde sha256 yerine yavaş ve tuzlu parola algoritmaları (bcrypt, argon2) tercih edilir; sha256'yı dosya bütünlüğü ve veri parmak izi için kullan.

random mı, secrets mi?Örneklem, simülasyon ve test verisi için random; token, tuz, geçici şifre, sıfırlama bağlantısı için secrets. random'ın çıktısı tahmin edilebilir, secrets'ınki işletim sisteminin kriptografik kaynağından gelir.
Derinleş

Derinleş: Standart kütüphanenin "gizli kahramanları"

İlk turda dosya sistemi, argparse, Decimal ve hashlib gibi büyük modülleri gördün. Ama iş yerinde en çok zaman kazandıran modüller çoğu zaman daha küçükler: sıralama anahtarını tek satırda veren operator, pahalı hesabı önbelleğe alan functools.lru_cache, eşik tablosunda arama yapan bisect, kopyalama tuzağını çözen copy, yazım hatasını düzelten difflib. PCAP sınavında ve mülakatlarda da sıkça çıkarlar. (collections ve itertools için ayrı bir ders var; tarih için tarih-saat, regex için regex dersine bak.)

İhtiyaçModülAnahtar araç
Sözlük/nesne listesini alana göre sıralaoperatoritemgetter, attrgetter
Aynı girdiyle tekrar tekrar çağrılan pahalı hesapfunctoolslru_cache, cache, cached_property
Eşik/kademe tablosu (kargo, vergi dilimi)bisectbisect_right, bisect_left, insort
İç içe veriyi bağımsız kopyalacopycopy, deepcopy
Benzer metni bul, iki metni karşılaştırdifflibget_close_matches, unified_diff
Basit ve güvenli metin şablonustringTemplate, safe_substitute
Benzersiz kimlik, ikili veriyi metne çevirme, sıkıştırmauuid, base64, zlib, gzipuuid4, b64encode, compress
Okunur döküm, hatayı bilinçli yutmapprint, contextlibpprint, suppress
Dış komut çalıştırmasubprocess, shlexrun, split, quote

operator ve functools: kısa ve hızlı fonksiyonlar

itemgetter("sehir", "tutar"), lambda s: (s["sehir"], s["tutar"]) ile aynı işi yapar ama daha okunur ve biraz daha hızlıdır. lru_cache ise bir fonksiyonun sonuçlarını argümanlara göre saklar; aynı argümanla ikinci çağrı hesap yapmadan döner. Yalnızca saf (pure) fonksiyonlarda kullan: sonucu saate, veritabanına ya da dış duruma bağlı fonksiyonu önbelleğe almak bayat veri döndürür. Argümanlar hashlenebilir olmalıdır (liste değil, demet).

from operator import itemgetter, attrgetter
from functools import lru_cache
from dataclasses import dataclass

siparisler = [
    {"no": "S-3", "sehir": "İzmir", "tutar": 80},
    {"no": "S-1", "sehir": "Ankara", "tutar": 120},
    {"no": "S-2", "sehir": "Ankara", "tutar": 45},
]
for s in sorted(siparisler, key=itemgetter("sehir", "tutar")):
    print(s["no"], s["sehir"], s["tutar"])

@dataclass
class Musteri:
    ad: str
    puan: int
print([m.ad for m in sorted([Musteri("Can", 3), Musteri("Ayşe", 9)], key=attrgetter("puan"), reverse=True)])

@lru_cache(maxsize=128)
def taksit_tablosu(tutar_kurus: int, ay: int) -> tuple:
    print("  hesaplanıyor:", tutar_kurus, ay)
    taksit = tutar_kurus // ay
    return tuple([taksit] * (ay - 1) + [tutar_kurus - taksit * (ay - 1)])

taksit_tablosu(100000, 3)
print(taksit_tablosu(100000, 3))      # ikinci çağrı önbellekten
print(taksit_tablosu.cache_info())
cache ve cached_propertyPython 3.9+ @functools.cache sınırsız lru_cache'tir. Sınıflarda bir kez hesaplanıp saklanacak özellik için @functools.cached_property kullan (ör. raporun toplamı). Önbelleği boşaltmak için fonk.cache_clear().

bisect: kademeli tablolarda arama

Kargo ücreti desiye, indirim sepet tutarına, vergi dilimi gelire göre değişir. Uzun bir if/elif zinciri yerine sıralı sınır listesi ve bisect kullanırsın: O(log n) arama yapar ve tablo değişince yalnızca listeyi güncellersin. bisect_right eşit değeri sağdaki dilime, bisect_left soldakine koyar; "1000 TL ve üzeri" gibi kurallarda fark burada çıkar.

from bisect import bisect_right, bisect_left, insort

sinirlar = [500, 1000, 2500]          # TL
oranlar = [0, 5, 10, 15]              # yüzde
for sepet in (499, 500, 999, 1000, 3000):
    print(sepet, "TL -> %", oranlar[bisect_right(sinirlar, sepet)])

print("bisect_left(1000):", bisect_left(sinirlar, 1000), "| bisect_right(1000):", bisect_right(sinirlar, 1000))

kuyruk = [10, 30, 50]
insort(kuyruk, 35)                    # sıralı listeye sırayı bozmadan ekle
print(kuyruk)

copy: yüzeysel ve derin kopya

copy.copy (ya da list.copy(), dict.copy()) yalnızca dış kabı kopyalar; içteki liste ve sözlükler paylaşılır. Şablon bir sepetten yeni sepet üretirken bu, bir müşterinin ürününün diğerinin sepetine düşmesi demektir. copy.deepcopy her seviyeyi kopyalar.

import copy

sablon = {"musteri": None, "kalemler": [], "kargo": {"firma": "Anadolu Kargo"}}
s1 = copy.copy(sablon)
s1["kalemler"].append("laptop")
print("yüzeysel kopya sonrası şablon:", sablon["kalemler"])

sablon["kalemler"].clear()
s2 = copy.deepcopy(sablon)
s2["kalemler"].append("mouse")
s2["kargo"]["firma"] = "Hızlı Kargo"
print("derin kopya sonrası şablon:", sablon)

difflib, string.Template, uuid, base64, zlib

Müşteri formuna "Ankra" yazdı; difflib.get_close_matches en yakın şehri önerir. string.Template kullanıcıların düzenleyebileceği e-posta şablonları için f-string'ten güvenlidir: şablon metni kod çalıştıramaz, yalnızca $ad gibi yer tutucuları doldurur. uuid4 tahmin edilemeyen benzersiz kimlik üretir; zlib + base64 ise ikili veriyi sıkıştırıp JSON'a ya da URL'ye konabilecek metne çevirir.

import difflib, uuid, base64, zlib
from string import Template

sehirler = ["Ankara", "Antalya", "Adana", "İzmir", "İstanbul"]
print(difflib.get_close_matches("Ankra", sehirler, n=1))
print(difflib.get_close_matches("Istanbul", sehirler, n=1, cutoff=0.6))

t = Template("Sayın $ad, $no numaralı siparişiniz $gun gün içinde teslim edilecek.")
print(t.substitute(ad="Ayşe Yılmaz", no="S-1001", gun=3))
print(t.safe_substitute(ad="Can"))     # eksik alanı olduğu gibi bırakır

kimlik = uuid.uuid4()
print("uuid uzunluğu:", len(str(kimlik)), "| sürüm:", kimlik.version)

rapor = ("Ankara;120\n" * 200).encode("utf-8")
paket = base64.b64encode(zlib.compress(rapor)).decode("ascii")
print(len(rapor), "bayt ->", len(paket), "karakter")
print(zlib.decompress(base64.b64decode(paket)) == rapor)

eski = ["Ankara;120", "İzmir;80"]
yeni = ["Ankara;125", "İzmir;80"]
print("\n".join(difflib.unified_diff(eski, yeni, "dun.csv", "bugun.csv", lineterm="")))
base64 şifreleme değildirbase64 yalnızca bir kodlamadır; herkes geri çözer. Gizli veriyi korumak için şifreleme (ör. cryptography paketi) gerekir. Benzer şekilde uuid1 makinenin MAC adresini ve zamanı içerir; dışarıya açık kimliklerde uuid4 kullan.

subprocess ve shlex: dış komut çalıştırmak

Betikten git, pg_dump ya da başka bir program çalıştırman gerekir. subprocess.run doğru araçtır; tarayıcıda alt süreç açılamadığı için aşağıdaki kodu kendi bilgisayarında dene. Komutu her zaman liste olarak ver; shell=True ile kullanıcı girdisi birleştirmek komut enjeksiyonuna kapı açar.

import subprocess, shlex

sonuc = subprocess.run(["git", "log", "-1", "--format=%h %s"],
                       capture_output=True, text=True, check=True, timeout=10)
print(sonuc.stdout.strip())

print(shlex.split('rapor.py --sehir "Kahramanmaraş Merkez" --limit 5'))
print(shlex.quote("dosya adı; rm -rf /"))   # kabuğa güvenli metin

Kendi bilgisayarında dene: check=True sıfır dışı çıkış kodunda CalledProcessError fırlatır, timeout asılı kalan komutu keser. Komut bulunamazsa FileNotFoundError alırsın; çoğu zaman program PATH'te değildir.

Gerçek işteAnadolu Market'in gece çalışan rapor betiği tamamen standart kütüphaneyle yazılmıştır: argparse ile tarih alır, subprocess ile veritabanı yedeği tetikler, bisect ile kargo kademesini hesaplar, lru_cache ile döviz kurunu tek sefer çeker, sonucu gzip ile sıkıştırıp uuid4 adlı dosyaya yazar. Sunucuya hiç paket kurmadan çalıştığı için güvenlik ekibinin onayı da çabuk alınmıştır. İlk soru hep şudur: "Bu iş için standart kütüphanede bir şey var mı?"
Uzmanlaş

Standart kütüphane ne zaman yetmez?

Tur bitti; şimdi sınırlar. Bir işi standart kütüphaneyle çözebiliyorsan dış paket ekleme: bağımlılık, güvenlik ve sürüm yükü taşırsın. Ama şu durumlarda dış pakete geçmek doğru karardır: argparse ile 20'den fazla alt komut yönetiyorsan typer ya da click; binlerce satırlık istatistikte statistics yerine numpy ve pandas; parola saklamada hashlib yerine argon2-cffi veya bcrypt; karmaşık tarih işlerinde dateutil. Karar ölçütün şu: "Standart kütüphane bu işi yapabiliyor mu ve kod hâlâ okunur mu?"

Bir de kurumsal kodda sık görülen üç küçük kalıbı aklında tut. Birincisi, betiğin her zaman main(argv=None) fonksiyonuyla başlaması: argv None ise sys.argv[1:] kullanır, değilse verileni; böylece aynı fonksiyon hem terminalden hem testten çağrılır. İkincisi, para alanlarını veritabanından okuduğun anda Decimal'e çevirip raporun sonuna kadar Decimal tutmak; float'a yalnızca grafik çizerken dönmek. Üçüncüsü, Enum üyelerini veritabanında .name olarak saklamak; sayısal .value saklarsan üye sırası değişince veri bozulur.

Alıştırmalara geç (21)