Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Path va sinflar oilasi
- 2.2. Qismlar
- 2.3. Birlashtirish va normallash
- 2.4. relative_to va is_relative_to
- 2.5. Fayl amallari
- 2.6. Qidiruv
- 2.7. Platforma farqlari
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Yo'l qurish va qismlar
- Misol 2 — Fayl amallari va qidiruv
- Misol 3 — Xavfsiz yo'l tekshiruvi
- Misol 4 — Amaliy: fotosuratlarni tartiblash vositasi
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
15.12-dars: pathlib
15-QISM — STANDART KUTUBXONA · 12-dars
1. Kirish va motivatsiya
Fayl yo'li — shunchaki satr emas. Unda qismlar (kataloglar, nom, kengaytma), platforma qoidalari (\ yoki /, harfga sezgirlik) va ma'no (absolyut yoki nisbiy, qaysi katalog ichida) bor. Satrlar bilan ishlaganda bularning hammasi qo'lda hisoblanadi — va xato qilinadi.
Real vaziyat. Fayl yuklash xizmati foydalanuvchi faylini shunday saqlardi:
yol = YUKLASH_KATALOGI + "/" + foydalanuvchi_fayl_nomi
if yol.startswith(YUKLASH_KATALOGI):
saqla(yol)Hujumchi fayl nomi sifatida ../../etc/cron.d/zararli yubordi. Satr YUKLASH_KATALOGI bilan boshlanardi — tekshiruv o'tdi, fayl esa katalogdan tashqariga yozildi. Bu path traversal hujumi. Yana bir xato: /srv/yuklash_eski ham "/srv/yuklash" bilan boshlanadi.
pathlib yo'lni obyekt sifatida ifodalaydi: qismlarga ajratadi, platforma qoidalarini biladi va resolve(), is_relative_to() kabi to'g'ri tekshiruvlarni beradi.
Bu darsda pathlib ni to'liq o'rganamiz: yo'l qurish va tahlil, fayl amallari, qidiruv va xavfsizlik.
Bu darsda:
-
PathvaPurePath: yo'l qurish/operatori bilan - Qismlar:
name,stem,suffix,suffixes,parent,parts with_name,with_stem,with_suffix- Absolyut yo'l bilan birlashtirish,
..varesolve() -
relative_to,is_relative_to— vastartswithtuzog'i - Fayl amallari:
read_text,write_text,mkdir,unlink,rename,replace - 3.14:
Path.copy,Path.move - Qidiruv:
glob,rglob,walk3.12-bob,full_match(3.13) - Platforma farqlari:
PurePosixPath,PureWindowsPath - Xavfsizlik: path traversal va fayl nomini tozalash
- Amaliy: fotosuratlarni sana bo'yicha tartiblash vositasi
2. Nazariya — chuqur tushuntirish
2.1. Path va sinflar oilasi
| Sinf | Fayl tizimiga murojaat | Qachon |
|---|---|---|
Path |
Oddiy ish — joriy platforma | |
PurePath |
Faqat yo'l ustida hisob | |
PurePosixPath |
Linux/macOS yo'llari (masalan, serverdagi yo'lni Windows'da tahlil qilish) | |
PureWindowsPath |
Windows yo'llari | |
PosixPath / WindowsPath |
Path() avtomatik shulardan birini yaratadi |
from pathlib import Path
p = Path("data") / "2026" / "sotuv.csv" # ⭐ / operatori
Path.cwd(); Path.home() # joriy va uy katalogios, shutil, open kabi funksiyalar Path ni to'g'ridan-to'g'ri qabul qiladi (os.PathLike). Satr kerak bo'lsa — str(p) yoki os.fspath(p).
2.2. Qismlar
Path("data/2026/arxiv.tar.gz") uchun:
| Atribut | Qiymat |
|---|---|
name |
"arxiv.tar.gz" |
stem |
"arxiv.tar" |
suffix |
".gz" |
suffixes |
[".tar", ".gz"] |
parent |
Path("data/2026") |
parents |
data/2026, data, . |
parts |
("data", "2026", "arxiv.tar.gz") |
anchor |
"" (nisbiy); "/" yoki "C:\\" (absolyut) |
Path(".bashrc").suffix — "", stem — ".bashrc": nuqta bilan boshlanadigan nom kengaytma emas.
Almashtirish (yangi obyekt qaytaradi):
| Metod | data/sotuv.csv dan |
|---|---|
with_name("x.txt") |
data/x.txt |
with_stem("xarajat") |
data/xarajat.csv |
with_suffix(".json") |
data/sotuv.json |
with_suffix("") |
data/sotuv |
with_suffix("json") |
ValueError: Invalid suffix — nuqta shart |
2.3. Birlashtirish va normallash
| Ifoda | Natija | Izoh |
|---|---|---|
Path("a") / "b" |
a/b |
|
Path("a") / "/b" |
/b |
Absolyut qism oldingisini tashlaydi |
Path("a/./b") |
a/b |
. olib tashlanadi |
Path("a/b/../c") |
a/b/../c |
.. saqlanadi (symlink tufayli) |
Path("a/../b") == Path("b") |
False |
|
p.resolve() |
Absolyut, .. va symlink lar hal qilingan |
Xavfsizlik tekshiruvi uchun |
p.absolute() |
Absolyut, lekin .. saqlanadi |
Foydalanuvchi bergan qismni / bilan qo'shish xavfsiz emas: u absolyut bo'lishi yoki .. saqlashi mumkin.
2.4. relative_to va is_relative_to
Path("/home/aziz/proj/src/app.py").relative_to("/home/aziz/proj") # src/app.py
Path("/a/b").relative_to("/c") # ValueError
Path("/a/b").is_relative_to("/a") # True| Tekshiruv | /srv/yuklash_eski/x "/srv/yuklash" ichidami? |
|---|---|
str(p).startswith("/srv/yuklash") |
True — noto'g'ri |
p.is_relative_to("/srv/yuklash") |
False — qismlar bo'yicha |
Xavfsiz tekshiruv: avval resolve(), keyin is_relative_to(ildiz.resolve()).
2.5. Fayl amallari
| Metod | Nima qiladi |
|---|---|
exists(), is_file(), is_dir() |
Tekshiruv |
read_text(encoding=...), write_text(...) |
Butun matn — kichik fayllar uchun |
read_bytes(), write_bytes() |
Baytlar |
open(...) |
Katta fayllar, oqim |
mkdir(parents=True, exist_ok=True) |
Katalog yaratish |
touch(exist_ok=True) |
Bo'sh fayl yoki vaqtni yangilash |
unlink(missing_ok=True) |
Faylni o'chirish |
rmdir() |
Faqat bo'sh katalog |
rename(yangi) |
Qayta nomlash (Windows'da mavjud faylga — xato) |
replace(yangi) |
Ustiga yozadi |
copy(maqsad) / move(maqsad) |
3.14+: fayl va katalog nusxasi/ko'chirish; ota katalog mavjud bo'lishi kerak |
stat() |
Hajm, vaqtlar |
read_text va write_text ga doim encoding="utf-8" bering — aks holda Windows'da tizim kodlashi ishlatiladi (16.1-dars).
exists() tekshiruvi va keyingi amal orasida fayl o'zgarishi mumkin (TOCTOU). "Yo'q bo'lsa yarat" — exist_ok, missing_ok yoki open(..., "x").
2.6. Qidiruv
| Metod | Nima qiladi |
|---|---|
iterdir() |
Bevosita ichidagilar |
glob("*.py") |
Shu katalogda naqsh bo'yicha |
glob("**/*.py") |
Rekursiv |
rglob("*.py") |
glob("**/*.py") bilan bir xil |
walk() |
3.12+: os.walk ning Path versiyasi — (katalog, ichki, fayllar) |
match("*.py") |
Yo'l oxiridan naqsh bilan solishtirish |
full_match("**/*.py") |
3.13+: butun yo'l bilan solishtirish |
| Naqsh | Ma'nosi |
|---|---|
* |
Bitta qismdagi istalgan belgilar |
? |
Bitta belgi |
[abc] |
Belgilar to'plami |
** |
Istalgan chuqurlikdagi kataloglar |
rglob yashirin kataloglarga ham (.git, .venv) kiradi va natija tartibi kafolatlanmagan — kerak bo'lsa sorted(). Katta daraxtlarda kesish kerak bo'lsa — walk() va ichki[:] = ... (15.11-dars).
2.7. Platforma farqlari
PurePosixPath |
PureWindowsPath |
|
|---|---|---|
| Ajratgich | / |
\ (lekin / ham qabul qilinadi) |
| Harfga sezgirlik (taqqoslash) | Farqli | Farqsiz |
| Anchor | / |
C:\, \\server\share\ |
"C:/x" / "D:/y" |
— | D:\y |
Ma'lumotlar bazasi, JSON yoki URL uchun yo'lni as_posix() bilan saqlang — platformadan mustaqil.
3. Tez ma'lumotnoma
from pathlib import Path
ildiz = Path(__file__).resolve().parent
sozlama = ildiz / "config" / "app.toml"
sozlama.parent.mkdir(parents=True, exist_ok=True)
matn = sozlama.read_text(encoding="utf-8")
sozlama.with_suffix(".bak").write_text(matn, encoding="utf-8")
for f in sorted(ildiz.rglob("*.py")): ...
f.relative_to(ildiz).as_posix()
yol = (YUKLASH / nom).resolve()
if not yol.is_relative_to(YUKLASH.resolve()): raise ValueError("ruxsat yo'q")Qoidalar
yo'l — Path, satr emas
Path / "/absolyut" — oldingi qism yo'qoladi
xavfsizlik: resolve() + is_relative_to(), startswith emas
read_text/write_text — encoding="utf-8"
rglob yashirin kataloglarga ham kiradi
saqlash va almashinuv — as_posix()4. Batafsil misollar
Misol 1 — Yo'l qurish va qismlar
"""/ operatori; name, stem, suffix, suffixes, parent, parents, parts; with_* metodlari; absolyut qism va .. ; resolve; Pure yo'llar."""
from pathlib import Path, PurePosixPath, PureWindowsPath
def main() -> None:
print("=== 1. Qurish va qismlar ===")
p = PurePosixPath("data") / "2026" / "arxiv.tar.gz"
print(f" yo'l: {p}")
print(f" name={p.name!r}, stem={p.stem!r}, suffix={p.suffix!r}, suffixes={p.suffixes}")
print(f" parent={p.parent}, parts={p.parts}")
print(f" parents: {[str(x) for x in p.parents]}")
print(f" ⚠️ '.bashrc': suffix={PurePosixPath('.bashrc').suffix!r}, stem={PurePosixPath('.bashrc').stem!r}")
print("\n=== 2. with_* metodlari ===")
fayl = PurePosixPath("hisobot/sotuv.csv")
print(f" with_name('x.txt') → {fayl.with_name('x.txt')}")
print(f" with_stem('xarajat') → {fayl.with_stem('xarajat')}")
print(f" with_suffix('.json') → {fayl.with_suffix('.json')}")
print(f" with_suffix('') → {fayl.with_suffix('')}")
try:
fayl.with_suffix("json")
except ValueError as xato:
print(f" with_suffix('json') → ValueError: {xato}")
print("\n=== 3. ⚠️ Birlashtirish tuzoqlari ===")
print(f" 'yuklash' / 'rasm.png' → {PurePosixPath('yuklash') / 'rasm.png'}")
print(f" 'yuklash' / '/etc/passwd' → {PurePosixPath('yuklash') / '/etc/passwd'} ← oldingi qism yo'qoldi")
print(f" 'a/./b' → {PurePosixPath('a/./b')}, 'a/b/../c' → {PurePosixPath('a/b/../c')} ← .. saqlanadi")
print(f" Path('a/../b') == Path('b'): {PurePosixPath('a/../b') == PurePosixPath('b')}")
print("\n=== 4. resolve() ===")
asosiy = Path.cwd()
chalkash = asosiy / "a" / "b" / ".." / ".." / "c.txt"
print(f" resolve() '..' larni hal qildi: {chalkash.resolve() == (asosiy / 'c.txt').resolve()}")
print(f" absolute() esa saqlaydi: {'..' in chalkash.absolute().parts}")
print("\n=== 5. Platforma yo'llari ===")
print(f" Windows: 'C:/Users/Aziz' == 'c:/users/aziz' → {PureWindowsPath('C:/Users/Aziz') == PureWindowsPath('c:/users/aziz')}")
print(f" POSIX: '/home/Aziz' == '/home/aziz' → {PurePosixPath('/home/Aziz') == PurePosixPath('/home/aziz')}")
print(f" Windows anchor: {PureWindowsPath('C:/x/y').anchor!r}, POSIX anchor: {PurePosixPath('/x/y').anchor!r}")
print(f" Windows yo'li as_posix(): {PureWindowsPath('C:\\Loyiha\\src\\app.py').as_posix()}")
print(f" Path turi shu tizimda: {type(Path('x')).__name__} (Linux/macOS'da PosixPath)")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Qurish va qismlar ===
yo'l: data/2026/arxiv.tar.gz
name='arxiv.tar.gz', stem='arxiv.tar', suffix='.gz', suffixes=['.tar', '.gz']
parent=data/2026, parts=('data', '2026', 'arxiv.tar.gz')
parents: ['data/2026', 'data', '.']
⚠️ '.bashrc': suffix='', stem='.bashrc'
=== 2. with_* metodlari ===
with_name('x.txt') → hisobot/x.txt
with_stem('xarajat') → hisobot/xarajat.csv
with_suffix('.json') → hisobot/sotuv.json
with_suffix('') → hisobot/sotuv
with_suffix('json') → ValueError: Invalid suffix 'json'
=== 3. ⚠️ Birlashtirish tuzoqlari ===
'yuklash' / 'rasm.png' → yuklash/rasm.png
'yuklash' / '/etc/passwd' → /etc/passwd ← oldingi qism yo'qoldi
'a/./b' → a/b, 'a/b/../c' → a/b/../c ← .. saqlanadi
Path('a/../b') == Path('b'): False
=== 4. resolve() ===
resolve() '..' larni hal qildi: True
absolute() esa saqlaydi: True
=== 5. Platforma yo'llari ===
Windows: 'C:/Users/Aziz' == 'c:/users/aziz' → True
POSIX: '/home/Aziz' == '/home/aziz' → False
Windows anchor: 'C:\\', POSIX anchor: '/'
Windows yo'li as_posix(): C:/Loyiha/src/app.py
Path turi shu tizimda: WindowsPath (Linux/macOS'da PosixPath)Nima ko'rsatdi: 2.1, 2.2, 2.3, 2.7-bo'limlar.
Misol 2 — Fayl amallari va qidiruv
"""mkdir, write_text, read_text, touch, unlink, rmdir, rename, replace; 3.14 copy va move; iterdir, glob, rglob, walk; match va full_match; TOCTOU."""
import tempfile
from pathlib import Path
def korinish(yollar, ildiz: Path) -> list[str]:
return sorted(p.relative_to(ildiz).as_posix() for p in yollar)
def main() -> None:
with tempfile.TemporaryDirectory() as vaqtinchalik:
r = Path(vaqtinchalik)
print("=== 1. Yaratish va o'qish ===")
(r / "src" / "pkg").mkdir(parents=True)
(r / "src" / "pkg" / "yordamchi.py").write_text("def f(): ...\n", encoding="utf-8")
(r / "src" / "app.py").write_text("print('salom')\n", encoding="utf-8")
(r / "README.md").write_text("# Loyiha\n", encoding="utf-8")
(r / ".git" / "objects").mkdir(parents=True)
(r / ".git" / "objects" / "hook.py").write_text("", encoding="utf-8")
(r / "rasm.png").write_bytes(b"\x89PNG\r\n")
print(f" README: {(r / 'README.md').read_text(encoding='utf-8')!r}, hajm: {(r / 'README.md').stat().st_size} bayt")
print(f" rasm boshi: {(r / 'rasm.png').read_bytes()[:4]!r}")
try:
(r / "src").mkdir()
except FileExistsError:
print(" mkdir mavjud katalogga → FileExistsError; exist_ok=True bilan jim")
(r / "src").mkdir(exist_ok=True)
print("\n=== 2. O'chirish va nomlash ===")
vaqt = r / "vaqtinchalik.txt"
vaqt.touch()
vaqt.unlink()
vaqt.unlink(missing_ok=True)
print(f" unlink(missing_ok=True) — xato yo'q, mavjud: {vaqt.exists()}")
try:
(r / "src").rmdir()
except OSError as xato:
print(f" bo'sh bo'lmagan katalogni rmdir → {type(xato).__name__}")
eski = r / "README.md"
yangi = eski.rename(r / "OQIBCHIQ.md")
print(f" rename natijasi: {yangi.name}, eski mavjud: {eski.exists()}")
(r / "nusxa.md").write_text("eski", encoding="utf-8")
yangi.replace(r / "nusxa.md")
print(f" replace mavjud faylga yozdi: {(r / 'nusxa.md').read_text(encoding='utf-8')!r}")
print("\n=== 3. 3.14: copy va move ===")
(r / "src" / "app.py").copy(r / "app_nusxa.py")
(r / "src").copy(r / "src_zaxira")
print(f" fayl nusxasi: {(r / 'app_nusxa.py').read_text(encoding='utf-8')!r}")
print(f" katalog nusxasi: {korinish((r / 'src_zaxira').rglob('*'), r)}")
(r / "arxiv").mkdir() # ⚠️ move ota katalogni yaratmaydi
(r / "src_zaxira").move(r / "arxiv" / "src_2026")
print(f" ko'chirildi: {(r / 'arxiv' / 'src_2026' / 'app.py').exists()}, eski yo'q: {not (r / 'src_zaxira').exists()}")
print("\n=== 4. Qidiruv ===")
print(f" iterdir: {korinish(r.iterdir(), r)}")
print(f" glob('*.py'): {korinish(r.glob('*.py'), r)}")
print(f" rglob('*.py'): {korinish(r.rglob('*.py'), r)}")
print(" ⚠️ rglob .git ichiga ham kirdi")
yurish = []
for katalog, ichki, fayllar in r.walk():
ichki[:] = sorted(d for d in ichki if not d.startswith("."))
yurish.extend((katalog / f).relative_to(r).as_posix() for f in fayllar if f.endswith(".py"))
print(f" walk + kesish: {sorted(yurish)}")
print("\n=== 5. match va full_match ===")
yol = Path("src/pkg/yordamchi.py")
for naqsh in ("*.py", "pkg/*.py", "src/*.py", "src/**/*.py"):
print(f" {naqsh:14} match={yol.match(naqsh)!s:5} full_match={yol.full_match(naqsh)}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Yaratish va o'qish ===
README: '# Loyiha\n', hajm: 10 bayt
rasm boshi: b'\x89PNG'
mkdir mavjud katalogga → FileExistsError; exist_ok=True bilan jim
=== 2. O'chirish va nomlash ===
unlink(missing_ok=True) — xato yo'q, mavjud: False
bo'sh bo'lmagan katalogni rmdir → OSError
rename natijasi: OQIBCHIQ.md, eski mavjud: False
replace mavjud faylga yozdi: '# Loyiha\n'
=== 3. 3.14: copy va move ===
fayl nusxasi: "print('salom')\n"
katalog nusxasi: ['src_zaxira/app.py', 'src_zaxira/pkg', 'src_zaxira/pkg/yordamchi.py']
ko'chirildi: True, eski yo'q: True
=== 4. Qidiruv ===
iterdir: ['.git', 'app_nusxa.py', 'arxiv', 'nusxa.md', 'rasm.png', 'src']
glob('*.py'): ['app_nusxa.py']
rglob('*.py'): ['.git/objects/hook.py', 'app_nusxa.py', 'arxiv/src_2026/app.py', 'arxiv/src_2026/pkg/yordamchi.py', 'src/app.py', 'src/pkg/yordamchi.py']
⚠️ rglob .git ichiga ham kirdi
walk + kesish: ['app_nusxa.py', 'arxiv/src_2026/app.py', 'arxiv/src_2026/pkg/yordamchi.py', 'src/app.py', 'src/pkg/yordamchi.py']
=== 5. match va full_match ===
*.py match=True full_match=False
pkg/*.py match=True full_match=False
src/*.py match=False full_match=False
src/**/*.py match=True full_match=TrueNima ko'rsatdi: 2.5, 2.6-bo'limlar.
Misol 3 — Xavfsiz yo'l tekshiruvi
"""Path traversal hujumi; startswith tuzog'i; resolve + is_relative_to; relative_to xatosi; fayl nomini tozalash."""
import re
import tempfile
from pathlib import Path
def xato_tekshiruv(ildiz: str, nom: str) -> bool:
yol = ildiz + "/" + nom
return yol.startswith(ildiz)
def togri_tekshiruv(ildiz: Path, nom: str) -> Path | None:
yol = (ildiz / nom).resolve()
return yol if yol.is_relative_to(ildiz.resolve()) else None
def nom_tozala(nom: str) -> str:
asosiy = Path(nom.replace("\\", "/")).name # faqat oxirgi qism
asosiy = re.sub(r"[^\w.\-]", "_", asosiy) # xavfli belgilar
asosiy = asosiy.lstrip(".") or "fayl" # yashirin va bo'sh nomlar
return asosiy[:100]
def main() -> None:
with tempfile.TemporaryDirectory() as vaqtinchalik:
yuklash = Path(vaqtinchalik) / "yuklash"
yuklash.mkdir()
(Path(vaqtinchalik) / "yuklash_eski").mkdir()
nomlar = ["rasm.png", "hujjat/2026/shartnoma.pdf", "../yuklash_eski/sir.txt",
"../../etc/passwd", "/etc/passwd", "pkg/../rasm.png"]
print("=== 1. ⚠️ Satr bilan tekshiruv ===")
otdi, rad = "o'tdi", "rad"
for nom in nomlar:
print(f" {nom:28} startswith → {otdi if xato_tekshiruv(str(yuklash), nom) else rad}")
print("\n=== 2. ✅ resolve + is_relative_to ===")
for nom in nomlar:
natija = togri_tekshiruv(yuklash, nom)
korinish = natija.relative_to(yuklash.resolve()).as_posix() if natija else "❌ rad etildi"
print(f" {nom:28} → {korinish}")
print("\n=== 3. ⚠️ Prefiks bilan bir xil boshlanadigan katalog ===")
eski = (Path(vaqtinchalik) / "yuklash_eski" / "sir.txt").resolve()
print(f" str.startswith: {str(eski).startswith(str(yuklash.resolve()))}")
print(f" is_relative_to: {eski.is_relative_to(yuklash.resolve())}")
try:
eski.relative_to(yuklash.resolve())
except ValueError:
print(" relative_to → ValueError (ichida emas)")
print("\n=== 4. Fayl nomini tozalash ===")
for nom in ("../../etc/passwd", "C:\\Windows\\win.ini", ".htaccess", "hisobot 2026 (yakuniy).pdf",
"rasm<script>.png", "..", ""):
print(f" {nom!r:32} → {nom_tozala(nom)!r}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. ⚠️ Satr bilan tekshiruv ===
rasm.png startswith → o'tdi
hujjat/2026/shartnoma.pdf startswith → o'tdi
../yuklash_eski/sir.txt startswith → o'tdi
../../etc/passwd startswith → o'tdi
/etc/passwd startswith → o'tdi
pkg/../rasm.png startswith → o'tdi
=== 2. ✅ resolve + is_relative_to ===
rasm.png → rasm.png
hujjat/2026/shartnoma.pdf → hujjat/2026/shartnoma.pdf
../yuklash_eski/sir.txt → ❌ rad etildi
../../etc/passwd → ❌ rad etildi
/etc/passwd → ❌ rad etildi
pkg/../rasm.png → rasm.png
=== 3. ⚠️ Prefiks bilan bir xil boshlanadigan katalog ===
str.startswith: True
is_relative_to: False
relative_to → ValueError (ichida emas)
=== 4. Fayl nomini tozalash ===
'../../etc/passwd' → 'passwd'
'C:\\Windows\\win.ini' → 'win.ini'
'.htaccess' → 'htaccess'
'hisobot 2026 (yakuniy).pdf' → 'hisobot_2026__yakuniy_.pdf'
'rasm<script>.png' → 'rasm_script_.png'
'..' → 'fayl'
'' → 'fayl'Nima ko'rsatdi: 2.3, 2.4-bo'limlar.
Misol 4 — Amaliy: fotosuratlarni tartiblash vositasi
Telefondan ko'chirilgan "tartibsiz" katalogni tartibga keltiramiz: rasmlar YYYY/MM/ kataloglariga, bir xil nomli fayllar to'qnashuvsiz (_1, _2), takroriy fayllar (bir xil mazmun) o'chirilmasdan takrorlar/ ga, qo'llab-quvvatlanmaydigan fayllar joyida qoladi. Avval quruq ishga tushirish (dry run) rejasi, keyin bajarish va natijani tekshirish. Sana fayl nomidan olinadi (IMG_20260917_...).
"""rglob bilan qidirish; nomdan sana ajratish; with_stem bilan to'qnashuvsiz nom; xesh bilan takrorlarni aniqlash; quruq ishga tushirish rejasi; move va natijani tekshirish."""
import hashlib
import re
import tempfile
from collections import Counter
from pathlib import Path
KENGAYTMALAR = {".jpg", ".jpeg", ".png", ".heic"}
SANA = re.compile(r"(?<![0-9])(20[0-9]{2})([01][0-9])([0-3][0-9])(?![0-9])")
NAMUNA = {
"DCIM/Camera/IMG_20260917_101500.jpg": b"toshkent-1",
"DCIM/Camera/IMG_20260917_101501.jpg": b"toshkent-2",
"DCIM/Camera/IMG_20260301_090000.JPG": b"navruz",
"WhatsApp/Images/IMG-20260917-WA0001.jpg": b"toshkent-1",
"Downloads/IMG_20260917_101500.jpg": b"boshqa-rasm-bir-xil-nom",
"Downloads/screenshot.png": b"sanasiz",
"Downloads/hujjat.pdf": b"pdf",
"DCIM/.thumbnails/t1.jpg": b"kichik",
}
def xesh(yol: Path) -> str:
return hashlib.sha256(yol.read_bytes()).hexdigest()
def bosh_nom(maqsad: Path, band: set[Path]) -> Path:
nomzod, i = maqsad, 0
while nomzod.exists() or nomzod in band:
i += 1
nomzod = maqsad.with_stem(f"{maqsad.stem}_{i}")
return nomzod
def reja_tuz(manba: Path, natija: Path) -> list[tuple[Path, Path, str]]:
reja, band, korilgan = [], set(), {}
for fayl in sorted(manba.rglob("*")):
if not fayl.is_file() or any(q.startswith(".") for q in fayl.relative_to(manba).parts):
continue
if fayl.suffix.lower() not in KENGAYTMALAR:
continue
mazmun = xesh(fayl)
if mazmun in korilgan:
maqsad = bosh_nom(natija / "takrorlar" / fayl.name, band)
sabab = f"takror: {korilgan[mazmun].relative_to(manba).as_posix()}"
else:
korilgan[mazmun] = fayl
topildi = SANA.search(fayl.name.replace("-", ""))
katalog = natija / topildi[1] / topildi[2] if topildi else natija / "sanasiz"
maqsad = bosh_nom(katalog / fayl.name.lower(), band)
sabab = "sana" if topildi else "sana topilmadi"
band.add(maqsad)
reja.append((fayl, maqsad, sabab))
return reja
def main() -> None:
with tempfile.TemporaryDirectory() as vaqtinchalik:
manba, natija = Path(vaqtinchalik) / "telefon", Path(vaqtinchalik) / "Rasmlar"
for nisbiy, mazmun in NAMUNA.items():
yol = manba / nisbiy
yol.parent.mkdir(parents=True, exist_ok=True)
yol.write_bytes(mazmun)
print("=== 1. Quruq ishga tushirish (reja) ===")
reja = reja_tuz(manba, natija)
for fayl, maqsad, sabab in reja:
print(f" {fayl.relative_to(manba).as_posix():42} → {maqsad.relative_to(natija).as_posix():32} [{sabab}]")
print(f" hali hech narsa ko'chirilmadi: {not natija.exists()}")
print("\n=== 2. Bajarish ===")
for fayl, maqsad, _ in reja:
maqsad.parent.mkdir(parents=True, exist_ok=True)
fayl.move(maqsad)
tuzilma = sorted(p.relative_to(natija).as_posix() for p in natija.rglob("*") if p.is_file())
for yol in tuzilma:
print(f" {yol}")
print("\n=== 3. Tekshiruvlar ===")
qolganlar = sorted(p.relative_to(manba).as_posix() for p in manba.rglob("*") if p.is_file())
print(f" manbada qolganlar: {qolganlar}")
print(f" hech bir fayl yo'qolmadi: {len(tuzilma) + len(qolganlar) == len(NAMUNA)}")
oylar = Counter(Path(y).parts[0] + "/" + Path(y).parts[1] for y in tuzilma if y[0].isdigit())
print(f" oylar bo'yicha: {dict(sorted(oylar.items()))}")
nomlar = [Path(y).name for y in tuzilma]
print(f" to'qnashuv hal qilindi (_1 qo'shimchasi): {any('_1.' in n for n in nomlar)}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Quruq ishga tushirish (reja) ===
DCIM/Camera/IMG_20260301_090000.JPG → 2026/03/img_20260301_090000.jpg [sana]
DCIM/Camera/IMG_20260917_101500.jpg → 2026/09/img_20260917_101500.jpg [sana]
DCIM/Camera/IMG_20260917_101501.jpg → 2026/09/img_20260917_101501.jpg [sana]
Downloads/IMG_20260917_101500.jpg → 2026/09/img_20260917_101500_1.jpg [sana]
Downloads/screenshot.png → sanasiz/screenshot.png [sana topilmadi]
WhatsApp/Images/IMG-20260917-WA0001.jpg → takrorlar/IMG-20260917-WA0001.jpg [takror: DCIM/Camera/IMG_20260917_101500.jpg]
hali hech narsa ko'chirilmadi: True
=== 2. Bajarish ===
2026/03/img_20260301_090000.jpg
2026/09/img_20260917_101500.jpg
2026/09/img_20260917_101500_1.jpg
2026/09/img_20260917_101501.jpg
sanasiz/screenshot.png
takrorlar/IMG-20260917-WA0001.jpg
=== 3. Tekshiruvlar ===
manbada qolganlar: ['DCIM/.thumbnails/t1.jpg', 'Downloads/hujjat.pdf']
hech bir fayl yo'qolmadi: True
oylar bo'yicha: {'2026/03': 1, '2026/09': 3}
to'qnashuv hal qilindi (_1 qo'shimchasi): TrueNima ko'rsatdi: 2.2, 2.5, 2.6-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"Path('a') / '/b' — a/b" |
/b — absolyut qism oldingisini tashlaydi |
"Path .. ni avtomatik soddalashtiradi" |
Yo'q — resolve() kerak |
"startswith bilan katalog ichidami tekshirsa bo'ladi" |
yuklash_eski ham o'tadi; is_relative_to |
"'.bashrc' ning kengaytmasi .bashrc" |
suffix — '' |
"with_suffix('json') ishlaydi" |
Nuqta shart |
"rglob yashirin kataloglarni o'tkazib yuboradi" |
Kiradi |
"rename hamma joyda ustiga yozadi" |
Windows'da yo'q — replace |
"read_text() doim UTF-8" |
Tizim kodlashi — encoding bering |
6. Keng tarqalgan xatolar va yechimlari
1. Satrlarni qo'shish
yol = katalog + "/" + nom # ❌
yol = Path(katalog) / nom # ✅2. Foydalanuvchi nomini tekshirmaslik
(YUKLASH / nom).write_bytes(malumot) # ❌ path traversal
yol = (YUKLASH / nom).resolve()
if not yol.is_relative_to(YUKLASH.resolve()): raise PermissionError # ✅3. Kodlashsiz o'qish
Path("izoh.txt").read_text() # ⚠️ Windows'da cp1251
Path("izoh.txt").read_text(encoding="utf-8") # ✅4. Kengaytmani satr bilan kesish
nom.split(".")[0] # ❌ "arxiv.tar.gz", ".bashrc"
Path(nom).stem # ✅5. Skript yonidagi fayl
open("config.toml") # ❌ joriy katalogga bog'liq
Path(__file__).resolve().parent / "config.toml" # ✅6. exists() bilan poyga
if not yol.exists(): yol.mkdir() # ⚠️ TOCTOU
yol.mkdir(exist_ok=True) # ✅7. Saqlashda platformaga bog'liq yo'l
baza.saqla(str(yol)) # ⚠️ Windows'da "\"
baza.saqla(yol.relative_to(ildiz).as_posix()) # ✅8. Katta katalogda rglob + .git
for f in ildiz.rglob("*"): ... # ⚠️ .git, node_modules
for katalog, ichki, fayllar in ildiz.walk(): ichki[:] = [...] # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 15.11-dars (o'tilgan):
os.walk,os.replace, platforma farqlari - 15.13-dars:
shutil— arxivlash, disk hajmi - 16-qism: fayl formatlari —
Pathbilan ochish - 17-qism:
pytesttmp_pathfixture'i —Pathobyekti - 19-qism: paket resurslari,
importlib.resources - 20.13-dars: fayl yuklash — path traversal
- 26.2-dars: fayllarni avtomatlashtirish
8. Eng yaxshi amaliyotlar
Yo'llar —
Path, satr faqat chegarada.Skript resurslari —
Path(__file__).resolve().parent.Foydalanuvchi bergan yo'l —
resolve()+is_relative_to(); nom — tozalangan.Matn — doim
encoding="utf-8".Saqlash va almashinuv —
as_posix(), nisbiy yo'l.Qidiruv natijalarini saralang.
Xavfli amallar oldidan quruq ishga tushirish rejasi.
exist_ok,missing_ok— tekshiruv va amal orasidagi poygasiz.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
from pathlib import PurePosixPath as P
1. print(P("a/b/c.txt").name)
2. print(P("a/b/c.tar.gz").suffix)
3. print(P("a/b/c.tar.gz").stem)
4. print(P("a") / "/b")
5. print(P("a/b").parent.parent)
6. print(P("x.csv").with_suffix(".json"))
7. print(P("/a/b/c").relative_to("/a"))
8. print(P("/ab").is_relative_to("/a"))
9. print(P(".env").suffix == "")
10. print(P("a/../b") == P("b"))
11. print(P("src/app.py").match("*.py"))
12. print(P("a/b/c").parts)Javoblar
c.txt.gzc.tar/b.x.jsonb/cFalseTrueFalseTrue('a', 'b', 'c')
Vazifa 2: Xatolarni tuzating
1. def saqla(nom, malumot):
yol = "/srv/yuklash/" + nom
if yol.startswith("/srv/yuklash"):
open(yol, "wb").write(malumot)
2. def yangi_nom(fayl):
return fayl.split(".")[0] + ".json"
3. def sozlama():
return open("config.toml").read()
4. def barcha_py(ildiz):
return list(Path(ildiz).rglob("*.py")) # .venv ichidagilar kerak emas
5. def zaxira(fayl):
Path(fayl).rename(fayl + ".bak") # .bak mavjud bo'lishi mumkinJavoblar
1. ILDIZ = Path("/srv/yuklash")
def saqla(nom, malumot):
yol = (ILDIZ / nom_tozala(nom)).resolve()
if not yol.is_relative_to(ILDIZ.resolve()):
raise PermissionError(nom)
yol.write_bytes(malumot)
2. def yangi_nom(fayl):
return Path(fayl).with_suffix(".json")
3. def sozlama():
return (Path(__file__).resolve().parent / "config.toml").read_text(encoding="utf-8")
4. def barcha_py(ildiz):
natija = []
for katalog, ichki, fayllar in Path(ildiz).walk():
ichki[:] = [d for d in ichki if d not in {".venv", ".git", "node_modules"}]
natija += [katalog / f for f in fayllar if f.endswith(".py")]
return sorted(natija)
5. def zaxira(fayl):
p = Path(fayl)
p.replace(p.with_name(p.name + ".bak"))Vazifa 3: Takroriy fayllar qidiruvchisi
- Katalogdagi barcha fayllarni rekursiv toping
- Avval hajm bo'yicha guruhlang (arzon), faqat bir xil hajmlilarning xeshini hisoblang
- Takroriy guruhlarni va tejash mumkin bo'lgan joyni ko'rsating
--ochirbayrog'i bilan eng eskisidan boshqalarini o'chirish; standart — quruq rejim- Katta fayllarni bo'laklab xeshlang (butun faylni xotiraga yuklamang)
Vazifa 4: Loyiha tozalovchi
__pycache__,*.pyc,.pytest_cache,build/,dist/,*.egg-infoni toping.venvva.gitichiga kirmang- Jami bo'shatiladigan hajmni ko'rsating
- Quruq rejim va tasdiqlash bilan o'chiring
- Chiqish kodi: xato bo'lsa 1
Vazifa 5: Statik sayt yig'uvchi
content/**/*.mdfayllarinisite/**/*.htmlga aylantiring (yo'l tuzilmasi saqlansin)- Faqat o'zgargan fayllarni qayta yig'ing (
stat().st_mtimesolishtirish) - O'chirilgan manba fayllar uchun eskirgan HTML larni olib tashlang
- Nisbiy havolalarni to'g'ri hisoblang (
relative_to,..)
Vazifa 6: Platformalararo yo'llar
- Windows'da yozilgan jurnal (
C:\Loyiha\src\app.py) va Linux jurnalini (/home/aziz/loyiha/src/app.py) bir xil nisbiy yo'lga keltiring PureWindowsPathvaPurePosixPathbilan, qaysi platformada ishlashidan qat'i nazar- Harfga sezgirlik farqini hisobga olib solishtiring
- 20 ta test bilan tekshiring
Vazifa 7: O'ylash
pathlib Python 3.4 da qo'shilgan, lekin ko'p yillar davomida os.path ustun bo'lib qoldi. Python 3.6 da os.PathLike protokoli qo'shilgach vaziyat o'zgardi. Nega "yangi, qulayroq API" darhol qabul qilinmadi va protokol qo'shilishi bunda qanday rol o'ynadi?
Javob
Qisqa javob: Yangi API ekotizim bilan o'zaro ishlamasa qabul qilinmaydi. 3.4–3.5 da Path obyektini ko'p funksiyalarga (open, os.*, shutil, uchinchi tomon kutubxonalar) to'g'ridan-to'g'ri berib bo'lmasdi — har joyda str(p) yozish kerak edi, bu esa qulaylikni yo'qqa chiqarardi. os.PathLike (PEP 519) har qanday funksiya "yo'lga o'xshash" obyektni qabul qilishining standart usulini berdi — va pathlib amalda standartga aylandi.
1. Muammo: 3.4–3.5
p = Path("data.csv")
open(p) # 3.5 da ishlardi, lekin ko'p kutubxonalar
shutil.copy(p, ...) # va os.* funksiyalar str kutardi
pandas.read_csv(p) # uchinchi tomonlar — noma'lumNatija: "Path bilan ishlab, chegaralarda str() ga o'tkazish" — yarim qulaylik.
2. PEP 519: os.PathLike
| Qism | Nima |
|---|---|
__fspath__() |
Obyektni str yoki bytes yo'lga aylantiruvchi metod |
os.fspath(obyekt) |
Standart o'giruvchi |
| Standart kutubxona | open, os, shutil va boshqalar PathLike qabul qiladigan qilindi |
| Kutubxona mualliflari | Bitta os.fspath() qo'shish kifoya |
Protokol bog'liqlikni teskari qildi: kutubxonalar pathlib ni import qilmaydi, faqat protokolni qo'llaydi — istalgan "yo'lga o'xshash" obyekt (masalan, zipfile.Path, bulut yo'llari) ishlaydi.
3. Qabul qilishni sekinlatgan boshqa omillar
- Tezlik: dastlabki
pathlibos.pathdan sezilarli sekin edi; keyingi versiyalarda optimallashtirildi - Odat va mavjud kod: millionlab qator
os.pathkodi - Python 2 bilan moslik: 2020-yilgacha ko'p loyihalar ikkala versiyani qo'llagan
- Kamchiliklar:
walk,copy,moveyo'q edi —os/shutilga qaytish kerak bo'lardi (3.12–3.14 da qo'shildi)
4. Umumiy tamoyil
| Yangi API muvaffaqiyati uchun | Misol |
|---|---|
| Mavjud ekotizim bilan protokol orqali ulanish | PathLike, __index__, __fspath__, buffer protokoli |
| Eski yo'l bilan birga yashash | os.path o'chirilmadi |
| To'liqlik | pathlib.walk/copy/move — os/shutil ga qaytmaslik |
| Tezlik | Yangi API sekin bo'lmasligi kerak |
5. Xulosa
- Qulay API yetarli emas — u ekotizimga "ulanishi" kerak
- Protokol (duck typing, 8.9 va 13.5-darslar) — bog'liqliksiz moslik
pathlibbugun — sukut tanlov,os.path— mavjud kod va maxsus holatlar uchun- O'z kutubxonangizda: aniq turni emas, protokolni (
os.PathLike) qabul qiling
Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.
Xulosa
Bu darsda fayl yo'llarini obyekt sifatida boshqarishni o'rgandik.
Eng muhim uch fikr:
Yo'l — obyekt.
Pathva/operatori yo'lni quradi,name,stem,suffix,suffixes,parent,partsqismlarni beradi,with_name/with_stem/with_suffixesa yangi yo'l yaratadi — satrlarni kesish va birlashtirish xatolarisiz. Saqlash va almashinuvdaas_posix()bilan platformadan mustaqil shakl; skript resurslari —Path(__file__).resolve().parent.Xavfsizlik:
resolve()+is_relative_to(). Foydalanuvchi bergan qism absolyut bo'lsa (/etc/passwd), oldingi yo'lni butunlay almashtiradi;..esaresolve()siz saqlanadi.startswithbilan tekshiruv../hujumini ham,yuklash_eskikabi o'xshash prefiksli kataloglarni ham o'tkazib yuboradi. Fayl nomi alohida tozalanadi.Fayl amallari va qidiruv bir joyda.
read_text/write_text(doimencoding="utf-8"),mkdir(parents=True, exist_ok=True),unlink(missing_ok=True),replace; 3.14 dacopyvamovekataloglar bilan ham ishlaydi.glob/rglobyashirin kataloglarga ham kiradi — kesish kerak bo'lsawalk()3.12-bob vaichki[:]. Xavfli ommaviy amallardan oldin — quruq ishga tushirish rejasi.
Keyingi darsda shutil moduliga o'tamiz: nusxa ko'chirish tafsilotlari, arxivlar, disk hajmi va dasturlarni topish.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!