Wiki 8: Advanced Python, Git & GitHub, na Mradi wa Mwezi 2
Mwezi 2 | Lengo kuu: Mwanafunzi aandike Python ya "kisasa" (comprehensions, lambda, generators, decorators), atumie Git & GitHub, na akamilishe Mradi wa Mwezi 2: "Mfumo wa Maktaba" uliohifadhiwa GitHub.
Maandalizi ya Mwalimu (soma kabla ya wiki kuanza)
- Sakinisha kabla ya Kipindi 1 (kompyuta ZOTE mbili kama inawezekana, na ya mwanafunzi):
- Git for Windows: https://git-scm.com/download/win (bonyeza Next kwa defaults; chagua "Visual Studio Code" kama default editor ikiulizwa).
- Thibitisha kwenye PowerShell:
git --version(mf.git version 2.47.0.windows.1). - Akaunti ya GitHub (https://github.com) — wewe na mwanafunzi. Mwambie mwanafunzi aitengeneze kabla ya Kipindi 2 na ahakiki email yake.
- Soma:
- Comprehensions: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
- Generators: https://docs.python.org/3/howto/functional.html#generators
- Decorators (glossary): https://docs.python.org/3/glossary.html#term-decorator
- GitHub "Hello World": https://docs.github.com/en/get-started/start-your-journey/hello-world
- Pro Git kitabu (sura 1–2, bure): https://git-scm.com/book/en/v2
- Fanya mwenyewe kwanza: tengeneza repo ya majaribio,
commit, napushkwenye GitHub angalau mara moja kabla ya Kipindi 2. Kosa la kawaida la kwanza ni la uthibitisho (login) — utalijua mwenyewe kabla ya darasa. - Mradi: endesha
maktaba(Kipindi 3) yote kabla ya Kipindi 3 ili ujue kila sehemu. - Andaa folder:
python_kozi\wiki08\.
Malengo ya Wiki (mwisho wa wiki mwanafunzi ataweza...)
- Kuandika list, dict na set comprehensions (pamoja na
if). - Kutumia
lambdapamoja nasorted(key=...),map,filter,max/min(key=...). - Kueleza iterator ni nini (
iter,next,StopIteration) na kuandika generator kwayield. - Kuandika decorator rahisi (pamoja na
*args, **kwargsnafunctools.wraps). - Kutumia Git:
init,status,add,commit,log,.gitignore. - Kutumia GitHub: kutengeneza repo,
remote add,push. - Kujenga na kupakia GitHub mradi wa maktaba unaotumia OOP + JSON + exceptions.
Kipindi 1 — Comprehensions, lambda, map/filter/sorted
Mtiririko wa kipindi
- Marudio ya kazi ya nyumbani ya Wiki 7 (hasa
Rafu— tutaihitaji Kipindi 3) - List comprehensions (+
if, +if/else) - Dict na set comprehensions; generator expression ndani ya
sum() - Mapumziko
lambdasorted(key=...),max/min(key=...),map,filter- Zoezi darasani
- Q&A + kazi ya nyumbani
Elezo kwa mwalimu (soma kwanza wewe)
List comprehension ni njia fupi ya kutengeneza list mpya kutoka iterable:
[KITU for kipengele in iterable] [KITU for kipengele in iterable if SHARTI] # chuja [A if SHARTI else B for kipengele in iterable] # badilisha kwa masharti
Ni sawa kabisa na loop ya for + append. Kanuni ya kusoma: anza na for, kisha if, mwisho angalia kitu cha mwanzo ("kwa kila x kwenye namba, kama x ni shufwa, chukua x*x").
Tahadhari ya mpangilio: if ya kuchuja inakuja mwishoni; if/else ya kubadilisha inakuja mwanzoni (kabla ya for). [x for x in n if x > 0 else 0] ni SyntaxError.
- Dict comprehension:
{k: v for ... in ...}→ dict. - Set comprehension:
{x for ... in ...}→ set (bila marudio). - Generator expression:
(x for ...)— haitengenezi list nzima; inatoa kimoja kimoja. Ndani ya function:sum(x * x for x in namba)(bila mabano ya ziada). - Usitumie comprehension ndefu sana — kama inazidi mstari mmoja na inakuwa ngumu kusoma, rudi kwenye loop ya kawaida.
lambda ni function ndogo isiyo na jina, yenye expression moja (hakuna return, hakuna mistari mingi):
mraba = lambda x: x * x # sawa na def mraba(x): return x * x
Kwa kawaida hatuiweki kwenye variable (PEP 8 inasema tumia def); tunaitumia papo hapo kama argument kwa sorted, max, map, filter.
sorted(iterable, key=f, reverse=False) — key ni function inayoitwa kwa kila kipengele, na kupanga kunafanywa kwa thamani inayorudishwa. Mf. sorted(maneno, key=len). Pia list.sort(key=...) (inabadilisha list yenyewe, inarudisha None). max(..., key=...) na min(..., key=...) vivyo hivyo.
map(f, iterable) — inaita f kwa kila kipengele. filter(f, iterable) — inabaki na vipengele ambavyo f inarudisha True. Zote zinarudisha iterator (si list) — lazima list(...) kuona matokeo. Kwa Python ya kisasa, comprehensions mara nyingi zinasomeka vizuri zaidi kuliko map/filter — lakini lazima uzijue kwa sababu utaziona.
Sema hivi kwa mwanafunzi
- "Comprehension ni kama kumwambia msaidizi kwa sentensi moja: 'Kwa kila mwanafunzi darasani, kama amefaulu, niletee jina lake.' Badala ya kumpa maagizo hatua kwa hatua."
- "
lambdani kama kuandika kwenye kikaratasi kidogo (sticky note) — maagizo mafupi ya mara moja, hayahitaji daftari zima (def)." - "
key=ni kumwambiasorted'panga kwa nini': kwa urefu? kwa bei? kwa umri? Ni kama kupanga wanafunzi mstarini kwa urefu wao badala ya kwa majina."
Andika code hii live (share screen)
Hatua 1: Loop vs comprehension — comprehensions.py
namba = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # Njia ya zamani mraba = [] for n in namba: mraba.append(n * n) print(mraba) # Comprehension mraba = [n * n for n in namba] print(mraba) shufwa = [n for n in namba if n % 2 == 0] print(shufwa) hali = ["shufwa" if n % 2 == 0 else "witiri" for n in namba[:4]] print(hali)
Output:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] [2, 4, 6, 8, 10] ['witiri', 'shufwa', 'witiri', 'shufwa']
Hatua 2: Mifano halisi
majina = [" asha ", "JUMA", "neema ", ""] safi = [j.strip().title() for j in majina if j.strip()] print(safi) # ['Asha', 'Juma', 'Neema'] bei_usd = [10, 25, 40] bei_tzs = [b * 2600 for b in bei_usd] print(bei_tzs) # [26000, 65000, 104000] maandishi = "python ni rahisi na python ni nzuri" herufi_za_kwanza = {neno[0] for neno in maandishi.split()} print(sorted(herufi_za_kwanza)) # ['n', 'p', 'r'] urefu = {neno: len(neno) for neno in ["embe", "nanasi", "papai"]} print(urefu) # {'embe': 4, 'nanasi': 6, 'papai': 5} alama = {"Asha": 88, "Juma": 42, "Neema": 67} waliofaulu = {jina: a for jina, a in alama.items() if a >= 50} print(waliofaulu) # {'Asha': 88, 'Neema': 67} print(sum(a for a in alama.values())) # 197 (generator expression) jedwali = [[i * j for j in range(1, 4)] for i in range(1, 4)] print(jedwali) # [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
Hatua 3: lambda + sorted — lambda_demo.py
def mraba(x): return x * x mraba2 = lambda x: x * x print(mraba(5), mraba2(5)) # 25 25 maneno = ["nanasi", "embe", "papai", "ndizi"] print(sorted(maneno)) # kwa alfabeti print(sorted(maneno, key=len)) # kwa urefu print(sorted(maneno, key=len, reverse=True)) wanafunzi = [("Asha", 88), ("Juma", 42), ("Neema", 67)] print(sorted(wanafunzi, key=lambda w: w[1], reverse=True)) print(max(wanafunzi, key=lambda w: w[1])) bidhaa = [ {"jina": "Sukari", "bei": 3000}, {"jina": "Chumvi", "bei": 800}, {"jina": "Mafuta", "bei": 7500}, ] nafuu_kwanza = sorted(bidhaa, key=lambda b: b["bei"]) print([b["jina"] for b in nafuu_kwanza])
Output:
25 25
['embe', 'nanasi', 'ndizi', 'papai']
['embe', 'papai', 'ndizi', 'nanasi']
['nanasi', 'papai', 'ndizi', 'embe']
[('Asha', 88), ('Neema', 67), ('Juma', 42)]
('Asha', 88)
['Chumvi', 'Sukari', 'Mafuta']
Eleza: sorted ni "stable" — maneno yenye urefu sawa (papai, ndizi) yanabaki kwa mpangilio wa awali.
Hatua 4: map na filter
namba = [1, 2, 3, 4, 5] print(map(lambda x: x * 10, namba)) # <map object at 0x...> print(list(map(lambda x: x * 10, namba))) # [10, 20, 30, 40, 50] print(list(filter(lambda x: x % 2 == 1, namba))) # [1, 3, 5] maandishi = ["12", "7", "30"] print(list(map(int, maandishi))) # [12, 7, 30] print(sum(map(int, input("Andika namba kwa nafasi: ").split())))
Sema: "map(int, ...) ni njia ya haraka sana ya kugeuza strings kuwa namba — ukumbuke input().split()."
Hatua 5: Objects (Wiki 7) + key
from dataclasses import dataclass @dataclass class Mwanafunzi: jina: str alama: int darasa = [Mwanafunzi("Asha", 88), Mwanafunzi("Juma", 42), Mwanafunzi("Neema", 67)] for m in sorted(darasa, key=lambda m: m.alama, reverse=True): print(m.jina, m.alama) print("Wastani:", sum(m.alama for m in darasa) / len(darasa))
Maswali ya kumuuliza mwanafunzi
- Andika
[x * 2 for x in range(5)]inatoa nini? —[0, 2, 4, 6, 8]. ifya kuchuja inakaa wapi kwenye comprehension? — Mwishoni, baada yafor.{x for x in [1, 1, 2]}inatoa nini? — Set{1, 2}.- Lambda inaweza kuwa na mistari mingi? — Hapana, expression moja tu.
key=lenkwenyesortedinamaanisha nini? — Panga kwa urefu wa kila kipengele.- Kwa nini
print(map(...))inaonyesha<map object>? —mapinarudisha iterator; tumialist(). - Tofauti ya
sorted(lst)nalst.sort()? —sortedinarudisha list mpya;.sort()inabadilisha ile ile na kurudishaNone.
Makosa ya kawaida
| Kosa | Ujumbe wa error | Suluhisho |
|---|---|---|
if/else mwishoni: [x for x in n if x > 0 else 0] |
SyntaxError: invalid syntax (au expected 'else' after 'if' expression) |
[x if x > 0 else 0 for x in n] |
lambda yenye return |
SyntaxError: invalid syntax |
lambda x: x * 2 (bila return) |
x = lst.sort() kisha kutumia x |
TypeError: 'NoneType' object is not iterable |
x = sorted(lst) |
key=len() (kuita function) |
TypeError: len() takes exactly one argument (0 given) |
key=len bila () |
| Kutumia map object mara mbili | Mara ya pili ni tupu [] |
Iterator inaisha; ihifadhi kama list(...). |
| Kupanga list yenye str na int pamoja | TypeError: '<' not supported between instances of 'int' and 'str' |
Geuza zote kwa aina moja, au key=str. |
Zoezi darasani
Zoezi 1.1: Kutoka joto_c = [22, 25, 31, 18, 29] tengeneza list ya Fahrenheit kwa comprehension, na list ya siku zenye joto zaidi ya 26°C.
Zoezi 1.2: Kutoka sentensi, tengeneza dict ya neno: idadi_ya_herufi kwa maneno yenye herufi zaidi ya 3 tu.
Zoezi 1.3: bidhaa = [("Sukari", 3000, 20), ("Mchele", 2500, 6), ("Sabuni", 1200, 3), ("Mafuta", 7500, 12)] (jina, bei, idadi). (a) Panga kwa thamani ya stock (bei×idadi) kutoka kubwa. (b) Tafuta bidhaa ya bei ghali zaidi kwa max(key=...). (c) Majina ya bidhaa zenye idadi < 10 kwa filter kisha kwa comprehension.
Jibu
# 1.1 joto_c = [22, 25, 31, 18, 29] joto_f = [c * 9 / 5 + 32 for c in joto_c] print(joto_f) # [71.6, 77.0, 87.8, 64.4, 84.2] print([c for c in joto_c if c > 26]) # [31, 29] # 1.2 sentensi = "Leo ni siku nzuri ya kujifunza Python" print({n: len(n) for n in sentensi.split() if len(n) > 3}) # {'siku': 4, 'nzuri': 5, 'kujifunza': 9, 'Python': 6} # 1.3 bidhaa = [("Sukari", 3000, 20), ("Mchele", 2500, 6), ("Sabuni", 1200, 3), ("Mafuta", 7500, 12)] print(sorted(bidhaa, key=lambda b: b[1] * b[2], reverse=True)) # [('Mafuta', 7500, 12), ('Sukari', 3000, 20), ('Mchele', 2500, 6), ('Sabuni', 1200, 3)] print(max(bidhaa, key=lambda b: b[1])) # ('Mafuta', 7500, 12) print([b[0] for b in filter(lambda b: b[2] < 10, bidhaa)]) # ['Mchele', 'Sabuni'] print([b[0] for b in bidhaa if b[2] < 10]) # ['Mchele', 'Sabuni']
Kipindi 2 — Iterators, Generators, Decorators, *args/**kwargs na Git & GitHub
Mtiririko wa kipindi
- Marudio mafupi ya kazi ya nyumbani
- Iterators:
iter,next,StopIteration - Generators:
yield - Marudio ya
*args,**kwargs - Decorators
- Mapumziko
- Git: config, init, status, add, commit, log,
.gitignore - GitHub: tengeneza repo,
remote add,push - Q&A + kazi ya nyumbani (maandalizi ya mradi)
Kipindi hiki kina mambo mengi — kama muda unabana, punguza decorators kuwa mfano mmoja (@muda), lakini USIRUKE Git: Kipindi 3 tunaihitaji.
Elezo kwa mwalimu (soma kwanza wewe)
Iterable vs iterator. Iterable ni kitu unachoweza kuzunguka kwa for (list, str, dict, file). Iterator ni "kidole" kinachokumbuka uko wapi: it = iter(lst), kisha next(it) inatoa kinachofuata; vikiisha → StopIteration. for loop inafanya hivi nyuma ya pazia. Iterator ikishaisha haianzi tena (ndiyo maana map ya Kipindi 1 ilikuwa tupu mara ya pili).
Generator ni function yenye yield badala ya (au pamoja na) return. Ukiiita, haiendeshi code mara moja — inarudisha generator object (aina ya iterator). Kila next() inaendesha code mpaka yield inayofuata, inatoa thamani, na inasimama (pause) ikikumbuka hali yake. Faida: inatumia memory kidogo — inatoa kimoja kimoja badala ya kujenga list nzima (mf. kusoma file la GB 2 mstari kwa mstari, au mfululizo usio na mwisho).
*args na **kwargs (marudio ya Wiki 4): *args inakusanya positional arguments za ziada kuwa tuple; **kwargs inakusanya keyword arguments kuwa dict. Majina args/kwargs ni convention; * na ** ndizo muhimu. Pia zinatumika kufungua: f(*lst), f(**d).
Decorator ni function inayopokea function nyingine na kurudisha function mpya "iliyofunikwa" (wrapper) yenye tabia ya ziada — bila kubadilisha code ya function ya asili. Hatua za kuelewa:
- Functions ni objects — zinaweza kupitishwa kama arguments (tumefanya na
key=!). - Function inaweza kufafanuliwa ndani ya function nyingine na kurudishwa.
@decoratorjuu yadef fni kifupi chaf = decorator(f).
Wrapper inatumia *args, **kwargs ili ifanye kazi na function yoyote, na @functools.wraps(func) ili jina na docstring ya function ya asili zisipotee. Tumeshaona decorators: @property, @dataclass, @abstractmethod.
Git ni programu ya "version control" — inahifadhi historia ya mabadiliko ya code yako kama "picha" (commits). Unaweza kurudi nyuma, kuona nini kilibadilika, na kufanya kazi na wengine. GitHub ni tovuti inayohifadhi repositories za Git mtandaoni (backup + kushiriki + portfolio ya kazi). Git ≠ GitHub: Git iko kwenye kompyuta yako; GitHub ni "seva" mtandaoni.
Maeneo matatu ya Git:
Working directory --git add--> Staging area --git commit--> Repository (historia)
(unahariri files) (umechagua cha kuhifadhi) (picha imehifadhiwa)
--git push--> GitHub
Amri (PowerShell, ndani ya folder la mradi):
git config --global user.name "Jina Lako" # mara moja tu kwa kompyuta git config --global user.email "email@ya-github.com" git config --global init.defaultBranch main git init # anzisha repo (folder lililofichwa .git) git status # nini kimebadilika? git add jina.py # au: git add . (kila kitu) git commit -m "Ujumbe unaoeleza mabadiliko" git log --oneline # historia
.gitignore ni file lenye orodha ya files/folders Git isizifuatilie: .venv/, __pycache__/, *.pyc, .vscode/, na data binafsi. Litengenezwe kabla ya git add . ya kwanza.
Kupakia GitHub: kwenye github.com → "+" → New repository → jina (mf. maktaba) → Public → USICHAGUE "Add a README" (repo iwe tupu, ili kuepuka migongano) → Create. Kisha:
git remote add origin https://github.com/JINA_LAKO/maktaba.git git branch -M main git push -u origin main
Mara ya kwanza, Windows itafungua dirisha la Git Credential Manager — chagua "Sign in with your browser" na uruhusu. Baada ya hapo, git push peke yake inatosha. (GitHub haikubali password ya akaunti kwenye terminal tangu 2021 — browser login au Personal Access Token tu.)
Sema hivi kwa mwanafunzi
- "Iterator ni kama kitabu chenye alama ya ukurasa (bookmark): unasoma ukurasa mmoja, unaweka alama, kesho unaendelea pale pale. Ukimaliza kitabu, umemaliza."
- "Generator ni kama mpishi wa chapati anayekupa moja moja zikiiva, badala ya kusubiri apike 100 ndipo akuletee zote. Anapumzika (yield) baada ya kila moja mpaka uombe nyingine (next)."
- "Decorator ni kama kufunga zawadi kwa karatasi ya rangi: zawadi (function) haibadiliki ndani, lakini sasa ina kitu cha ziada nje."
- "Git ni kama kitufe cha 'Save Game' kwenye mchezo wa video. Kila commit ni sehemu unayoweza kurudi ukikosea. GitHub ni kuhifadhi 'save' zako kwenye cloud ili hata kompyuta ikiibiwa, kazi yako iko salama."
- "
git addni kuweka vitu kwenye boksi;git commitni kufunga boksi na kuandika lebo;git pushni kulituma posta."
Andika code hii live (share screen)
Hatua 1: Iterator — iterators.py
matunda = ["embe", "nanasi", "papai"] it = iter(matunda) print(next(it)) # embe print(next(it)) # nanasi print(next(it)) # papai try: print(next(it)) except StopIteration: print("Vimeisha!")
Hatua 2: Generator
def hesabu_hadi(n): print("Naanza...") i = 1 while i <= n: yield i i += 1 print("Nimemaliza.") g = hesabu_hadi(3) print(g) # <generator object hesabu_hadi at 0x...> print(next(g)) # Naanza... 1 print(next(g)) # 2 for x in g: # inaendelea pale ilipoachwa print("loop:", x)
Output:
<generator object hesabu_hadi at 0x...> Naanza... 1 2 loop: 3 Nimemaliza.
Sisitiza: "Naanza..." haikuchapishwa tulipoita hesabu_hadi(3) — ilisubiri next ya kwanza.
Mfano wa manufaa:
import sys def namba_shufwa(kikomo): for n in range(kikomo): if n % 2 == 0: yield n list_kubwa = [n for n in range(1_000_000) if n % 2 == 0] gen = namba_shufwa(1_000_000) print(sys.getsizeof(list_kubwa), "bytes") # takriban 4,000,000+ print(sys.getsizeof(gen), "bytes") # takriban 200 print(sum(namba_shufwa(1_000_000))) # 249999500000 def soma_mistari(path): with open(path, encoding="utf-8") as f: for mstari in f: mstari = mstari.strip() if mstari and not mstari.startswith("#"): yield mstari
(Namba halisi za getsizeof zinatofautiana kidogo kati ya matoleo — muhimu ni tofauti kubwa.)
Mfululizo usio na mwisho:
def fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b fib = fibonacci() print([next(fib) for _ in range(10)]) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Hatua 3: *args, **kwargs — args_kwargs.py
def jumla(*args): print("args:", args) return sum(args) print(jumla(1, 2, 3)) def wasifu(jina, **kwargs): print("kwargs:", kwargs) for k, v in kwargs.items(): print(f" {k}: {v}") wasifu("Asha", umri=17, mji="Arusha") namba = [4, 5, 6] print(jumla(*namba)) # kufungua list taarifa = {"umri": 20, "kazi": "Mwalimu"} wasifu("Juma", **taarifa) # kufungua dict
Output:
args: (1, 2, 3)
6
kwargs: {'umri': 17, 'mji': 'Arusha'}
umri: 17
mji: Arusha
args: (4, 5, 6)
15
kwargs: {'umri': 20, 'kazi': 'Mwalimu'}
umri: 20
kazi: Mwalimu
Hatua 4: Decorators — decorators.py. Jenga hatua kwa hatua:
# (a) functions ni objects def salimia(jina): return f"Habari {jina}" f = salimia print(f("Asha")) # (b) function inayorudisha function def tengeneza_kizidishi(n): def zidisha(x): return x * n return zidisha mara3 = tengeneza_kizidishi(3) print(mara3(10)) # 30
# (c) decorator kwa mkono def kelele(func): def wrapper(*args, **kwargs): jibu = func(*args, **kwargs) return jibu.upper() + "!!!" return wrapper def salimia(jina): return f"Habari {jina}" salimia = kelele(salimia) # kufunika kwa mkono print(salimia("Asha")) # HABARI ASHA!!! # (d) syntax ya @ -- sawa kabisa na hapo juu @kelele def aga(jina): return f"Kwaheri {jina}" print(aga("Juma")) # KWAHERI JUMA!!!
Sasa decorators za manufaa:
import functools import time def muda(func): """Inapima muda wa function kuendeshwa.""" @functools.wraps(func) def wrapper(*args, **kwargs): mwanzo = time.perf_counter() jibu = func(*args, **kwargs) mwisho = time.perf_counter() print(f"[{func.__name__}] ilichukua sekunde {mwisho - mwanzo:.4f}") return jibu return wrapper def rekodi(func): """Inaandika function ilipoitwa na arguments zake.""" @functools.wraps(func) def wrapper(*args, **kwargs): print(f"-> {func.__name__} args={args} kwargs={kwargs}") jibu = func(*args, **kwargs) print(f"<- {func.__name__} imerudisha {jibu!r}") return jibu return wrapper @muda def hesabu_kubwa(n): return sum(i * i for i in range(n)) @rekodi def ongeza(a, b=0): return a + b print(hesabu_kubwa(1_000_000)) ongeza(2, b=3) print(ongeza.__name__) # ongeza (shukrani kwa functools.wraps)
Output (muda utatofautiana):
[hesabu_kubwa] ilichukua sekunde 0.0612
333332833333500000
-> ongeza args=(2,) kwargs={'b': 3}
<- ongeza imerudisha 5
ongeza
Onyesha: ondoa @functools.wraps(func) → ongeza.__name__ inakuwa wrapper. "Ndiyo maana tunaweka wraps."
Hatua 5: Git — kwenye terminal ya VS Code, ndani ya wiki08:
git --version git config --global user.name "Mwalimu Selanim" git config --global user.email "email-yako@example.com" git config --global init.defaultBranch main git config --list
(Mwambie mwanafunzi atumie jina na email ya akaunti yake ya GitHub.)
Tengeneza folder la majaribio:
mkdir git_majaribio cd git_majaribio git init git status
Output: Initialized empty Git repository in C:/.../git_majaribio/.git/ kisha On branch main ... nothing to commit.
Tengeneza salamu.py (print("Habari Git!")), kisha:
git status # salamu.py ni "Untracked" (nyekundu) git add salamu.py git status # "Changes to be committed" (kijani) git commit -m "Ongeza programu ya salamu" git log --oneline
Badilisha salamu.py (ongeza mstari), kisha:
git status # modified git diff # inaonyesha mistari iliyobadilika (+ kijani, - nyekundu) git add . git commit -m "Ongeza salamu ya pili" git log --oneline
Output ya mfano:
b4e1c2a (HEAD -> main) Ongeza salamu ya pili 7a9d3f0 Ongeza programu ya salamu
.gitignore — tengeneza venv na uonyeshe tatizo:
python -m venv .venv git status # .venv/ inaonekana -- hatutaki!
Tengeneza file .gitignore (kwenye VS Code: New File → .gitignore):
.venv/ __pycache__/ *.pyc .vscode/
git status # .venv imetoweka; .gitignore yenyewe inaonekana git add .gitignore git commit -m "Ongeza .gitignore"
Onyesha pia kitufe cha Source Control (Ctrl+Shift+G) kwenye VS Code — kinafanya add/commit kwa kubofya. Lakini tunajifunza amri kwanza.
Hatua 6: GitHub — share browser: github.com → "+" → New repository → git_majaribio → Public → bila README → Create. Nakili amri za "…or push an existing repository from the command line":
git remote add origin https://github.com/JINA_LAKO/git_majaribio.git git branch -M main git push -u origin main
Login kwa browser ikiombwa. Refresh ukurasa wa GitHub — files zimefika! Badilisha kitu, add, commit, kisha git push peke yake.
Maswali ya kumuuliza mwanafunzi
- Kinatokea nini ukiita
next()kwenye iterator iliyoisha? —StopIteration. - Tofauti ya
returnnayield? —returninamaliza function;yieldinatoa thamani na kusimama, ikiendeleanextijayo. - Kwa nini generator inatumia memory kidogo? — Haijengi list nzima; inatoa kimoja kimoja.
*argsni aina gani ndani ya function?**kwargs? — Tuple; dict.@kelelejuu yadef agani sawa na nini? —aga = kelele(aga).- Kwa nini wrapper itumie
*args, **kwargs? — Ili ifanye kazi na function yoyote, yenye arguments zozote. - Hatua tatu za kuhifadhi mabadiliko kwenye GitHub? —
git add,git commit -m "...",git push. - Kwa nini
.venv/iwe kwenye.gitignore? — Ni kubwa na inaweza kutengenezwa upya kwarequirements.txt.
Makosa ya kawaida
| Kosa | Ujumbe wa error | Suluhisho |
|---|---|---|
| Git haijasakinishwa / terminal ya zamani | git : The term 'git' is not recognized as the name of a cmdlet... |
Sakinisha Git, funga na ufungue upya VS Code. |
| Hukuweka jina/email | Author identity unknown *** Please tell me who you are. |
git config --global user.name ... na user.email ... |
Commit bila -m |
Inafungua editor (Vim/VS Code) — mwanafunzi anakwama | Kwenye Vim: andika :q! Enter; kisha tumia -m "ujumbe". |
git commit bila add |
nothing added to commit but untracked files present |
git add . kwanza |
git init kwenye folder lisilo sahihi (mf. Documents nzima) |
Maelfu ya files "untracked" | Futa folder .git lililofichwa (Remove-Item -Recurse -Force .git) na cd kwenye folder sahihi. |
| Repo ya GitHub ina README, push inakataliwa | ! [rejected] main -> main (fetch first) |
git pull origin main --allow-unrelated-histories kisha git push, au tengeneza repo bila README. |
remote origin tayari ipo |
error: remote origin already exists. |
git remote set-url origin <URL sahihi> |
| Kosa la jina la branch | error: src refspec main does not match any |
Bado hakuna commit, au branch ni master: fanya commit kwanza / git branch -M main |
| Kuita generator na kutarajia list | print(gen) inaonyesha <generator object ...> |
list(gen) au for |
Decorator inasahau return jibu |
Function iliyopambwa inarudisha None |
return func(*args, **kwargs) |
Zoezi darasani
Zoezi 2.1: Generator hesabu_kurudi_nyuma(n) inayotoa n, n-1, ..., 1 kisha "Uzinduzi!".
Zoezi 2.2: Generator vipande(lst, ukubwa) inayogawa list kuwa vipande: list(vipande([1,2,3,4,5], 2)) → [[1, 2], [3, 4], [5]].
Zoezi 2.3: Decorator jaribu_tena(func) — kama function inarusha exception, ijaribu tena hadi mara 3 kabla ya kuacha (rusha kosa la mwisho).
Zoezi 2.4 (Git): Mwanafunzi apeleke kazi zake za Wiki 7 (folder wiki07) kwenye repo mpya ya GitHub python-kozi, akiwa na .gitignore na angalau commits 2.
Jibu
# 2.1 def hesabu_kurudi_nyuma(n): while n > 0: yield n n -= 1 yield "Uzinduzi!" for x in hesabu_kurudi_nyuma(3): print(x) # 3 2 1 Uzinduzi! # 2.2 def vipande(lst, ukubwa): for i in range(0, len(lst), ukubwa): yield lst[i:i + ukubwa] print(list(vipande([1, 2, 3, 4, 5], 2))) # [[1, 2], [3, 4], [5]] # 2.3 import functools import random def jaribu_tena(func): @functools.wraps(func) def wrapper(*args, **kwargs): kosa_la_mwisho = None for jaribio in range(1, 4): try: return func(*args, **kwargs) except Exception as e: print(f"Jaribio {jaribio} limeshindwa: {e}") kosa_la_mwisho = e raise kosa_la_mwisho return wrapper @jaribu_tena def mtandao_dhaifu(): if random.random() < 0.6: raise ConnectionError("Mtandao umekatika") return "Data imepakuliwa" try: print(mtandao_dhaifu()) except ConnectionError: print("Imeshindikana baada ya majaribio 3.")
2.4 — amri:
cd ..\wiki07 git init # tengeneza .gitignore (.venv/, __pycache__/, *.pyc, .vscode/) git add .gitignore git commit -m "Ongeza .gitignore" git add . git commit -m "Kazi za Wiki 7: inheritance na dataclasses" git remote add origin https://github.com/JINA_LAKO/python-kozi.git git branch -M main git push -u origin main
Kipindi 3 — Mradi wa Mwezi 2: "Mfumo wa Maktaba"
Mtiririko wa kipindi
- Marudio: Git ilifanya kazi?
Rafuya Wiki 7 iko tayari? - Maelezo ya mradi, muundo wa files,
git init+.gitignore models.py— dataclassesKitabu,Mwanachama+ exceptions (makosa.py)maktaba.py— classMaktaba(logic) + kuhifadhi/kupakia JSON- Mapumziko (commit kwanza!)
main.py— menu ya terminal, kushughulikia makosa- README, commit ya mwisho,
git pushkwenye GitHub - Tathmini ya Mwezi 2, maelekezo ya likizo.
Elezo kwa mwalimu (soma kwanza wewe)
Mradi huu unaunganisha kila kitu cha Mwezi 2:
| Wiki | Kinachotumika kwenye mradi |
|------|-----------------------------|
| 5 | JSON, pathlib, try/except, custom exceptions |
| 6 | Classes, methods, encapsulation (_vitabu), __str__ |
| 7 | @dataclass, inheritance ya exceptions, type hints, __len__ |
| 8 | Comprehensions, lambda + sorted, generator, decorator, Git/GitHub |
Muundo wa mradi:
maktaba/ ├── .gitignore ├── README.md ├── makosa.py # custom exceptions ├── models.py # Kitabu, Mwanachama (dataclasses) ├── maktaba.py # class Maktaba: logic + JSON ├── main.py # menu ya mtumiaji └── data.json # inaundwa yenyewe (iko kwenye .gitignore? -- tazama chini)
Uamuzi kuhusu data.json: tunaiweka kwenye .gitignore kwa sababu ni data ya mtumiaji, si code. (Mwambie mwanafunzi: kwenye mradi halisi, data ya watu haiwekwi GitHub.)
Jinsi ya kufundisha: Wewe unaandika makosa.py na models.py live huku ukieleza. maktaba.py andikeni pamoja — mwanafunzi aandike methods rudisha na tafuta mwenyewe ukimwongoza. main.py — mpe muundo, aandike sehemu ya menu. Commit baada ya kila file! Kama muda utaisha, mwanafunzi amalize wakati wa likizo (iko kwenye kazi ya hiari).
Mambo mapya madogo kwenye mradi:
asdict(obj)(kutokadataclasses) → dict kwa JSON;Kitabu(**d)→ kurudisha dict kuwa object (hapa ndipo**kwargsya Kipindi 2 inaonekana kwa manufaa!).@classmethod— method inayopokea class (cls) badala ya object; inatumiwa kama "constructor mbadala":Maktaba.pakia(path). Eleza: "Ni njia ya kuunda object kwa njia nyingine — hapa kutoka file."date.today().isoformat()→"2026-12-24"(string, inafaa JSON).date.fromisoformat(s)kurudisha.
Sema hivi kwa mwanafunzi
- "Leo wewe ni developer wa maktaba ya shule. Mkutubi anataka kompyuta ikumbuke vitabu vyote, wanachama, nani ameazima nini, na kitabu kimechelewa siku ngapi."
- "Tunagawa code kwenye files kama idara za kampuni:
modelsni idara ya kumbukumbu,maktabani meneja anayefanya maamuzi,mainni mapokezi yanayoongea na wateja." - "Commit kila hatua — kama kuhifadhi mchezo kabla ya level ngumu."
Andika code hii live (share screen)
Hatua 1: Anza mradi
mkdir maktaba cd maktaba git init code .gitignore
.gitignore:
.venv/ __pycache__/ *.pyc .vscode/ data.json
(Mradi huu hautumii packages za nje — requirements.txt si lazima. Kama mwanafunzi anataka, python -m venv .venv bado ni mazoea mazuri.)
Hatua 2: makosa.py
"""Makosa (exceptions) maalum ya mfumo wa maktaba.""" class KosaLaMaktaba(Exception): """Mzazi wa makosa yote ya maktaba.""" class KitabuHakipo(KosaLaMaktaba): pass class MwanachamaHayupo(KosaLaMaktaba): pass class KimeshaAzimwa(KosaLaMaktaba): pass class HakijaAzimwa(KosaLaMaktaba): pass class KikomoKimefikiwa(KosaLaMaktaba): pass class TayariKipo(KosaLaMaktaba): pass
Eleza: docstring ndani ya class inatosha — hatuhitaji pass kama kuna docstring.
Hatua 3: models.py
"""Data za mfumo: Kitabu na Mwanachama.""" from dataclasses import dataclass, field @dataclass class Kitabu: isbn: str kichwa: str mwandishi: str mwaka: int aliyeazima: str | None = None # namba ya mwanachama tarehe_ya_kuazima: str | None = None # "YYYY-MM-DD" @property def kimeazimwa(self) -> bool: return self.aliyeazima is not None def __str__(self) -> str: hali = f"kimeazimwa na {self.aliyeazima}" if self.kimeazimwa else "kipo" return f"[{self.isbn}] {self.kichwa} - {self.mwandishi} ({self.mwaka}) | {hali}" @dataclass class Mwanachama: namba: str jina: str vitabu: list[str] = field(default_factory=list) # ISBN za vitabu alivyoazima def __str__(self) -> str: return f"{self.namba}: {self.jina} (ameazima {len(self.vitabu)})"
Jaribu haraka kwenye terminal:
python -c "from models import Kitabu; k = Kitabu('1', 'Kinjeketile', 'E. Hussein', 1969); print(k)"
Output: [1] Kinjeketile - E. Hussein (1969) | kipo
git add . git commit -m "Ongeza models na makosa"
Hatua 4: maktaba.py (moyo wa mradi)
"""Logic ya maktaba na kuhifadhi kwenye JSON.""" import functools import json from dataclasses import asdict from datetime import date from pathlib import Path from typing import Iterator from makosa import ( HakijaAzimwa, KikomoKimefikiwa, KimeshaAzimwa, KitabuHakipo, MwanachamaHayupo, TayariKipo, ) from models import Kitabu, Mwanachama SIKU_ZA_KUAZIMA = 14 FAINI_KWA_SIKU = 200 # TSh def hifadhi_baadaye(method): """Decorator: baada ya method kufanikiwa, hifadhi maktaba kwenye file.""" @functools.wraps(method) def wrapper(self, *args, **kwargs): jibu = method(self, *args, **kwargs) self.hifadhi() return jibu return wrapper class Maktaba: KIKOMO_CHA_VITABU = 3 def __init__(self, path: Path): self.path = Path(path) self._vitabu: dict[str, Kitabu] = {} self._wanachama: dict[str, Mwanachama] = {} # ---------- kuhifadhi / kupakia ---------- def hifadhi(self) -> None: data = { "vitabu": [asdict(k) for k in self._vitabu.values()], "wanachama": [asdict(m) for m in self._wanachama.values()], } with open(self.path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) @classmethod def pakia(cls, path: Path) -> "Maktaba": maktaba = cls(path) try: with open(maktaba.path, encoding="utf-8") as f: data = json.load(f) except FileNotFoundError: return maktaba # maktaba mpya, tupu except json.JSONDecodeError: print("Onyo: data.json imeharibika, naanza na maktaba tupu.") return maktaba for d in data.get("vitabu", []): k = Kitabu(**d) maktaba._vitabu[k.isbn] = k for d in data.get("wanachama", []): m = Mwanachama(**d) maktaba._wanachama[m.namba] = m return maktaba # ---------- kutafuta ---------- def kitabu(self, isbn: str) -> Kitabu: try: return self._vitabu[isbn] except KeyError: raise KitabuHakipo(f"Hakuna kitabu chenye ISBN '{isbn}'.") from None def mwanachama(self, namba: str) -> Mwanachama: try: return self._wanachama[namba] except KeyError: raise MwanachamaHayupo(f"Hakuna mwanachama namba '{namba}'.") from None def tafuta(self, neno: str) -> list[Kitabu]: neno = neno.lower() return [ k for k in self._vitabu.values() if neno in k.kichwa.lower() or neno in k.mwandishi.lower() ] def vitabu_vilivyopangwa(self, kwa: str = "kichwa") -> list[Kitabu]: return sorted(self._vitabu.values(), key=lambda k: getattr(k, kwa)) def vinavyopatikana(self) -> list[Kitabu]: return [k for k in self._vitabu.values() if not k.kimeazimwa] def vilivyochelewa(self, leo: date | None = None) -> Iterator[tuple[Kitabu, int]]: """Generator: inatoa (kitabu, siku_za_kuchelewa) kwa vitabu vilivyopitiliza muda.""" leo = leo or date.today() for k in self._vitabu.values(): if k.tarehe_ya_kuazima: siku = (leo - date.fromisoformat(k.tarehe_ya_kuazima)).days if siku > SIKU_ZA_KUAZIMA: yield k, siku - SIKU_ZA_KUAZIMA # ---------- kubadilisha ---------- @hifadhi_baadaye def ongeza_kitabu(self, kitabu: Kitabu) -> None: if kitabu.isbn in self._vitabu: raise TayariKipo(f"ISBN '{kitabu.isbn}' tayari ipo.") self._vitabu[kitabu.isbn] = kitabu @hifadhi_baadaye def sajili(self, mwanachama: Mwanachama) -> None: if mwanachama.namba in self._wanachama: raise TayariKipo(f"Mwanachama '{mwanachama.namba}' tayari yupo.") self._wanachama[mwanachama.namba] = mwanachama @hifadhi_baadaye def azima(self, isbn: str, namba: str, leo: date | None = None) -> None: k = self.kitabu(isbn) m = self.mwanachama(namba) if k.kimeazimwa: raise KimeshaAzimwa(f"'{k.kichwa}' kimeshaazimwa na {k.aliyeazima}.") if len(m.vitabu) >= self.KIKOMO_CHA_VITABU: raise KikomoKimefikiwa( f"{m.jina} tayari ana vitabu {self.KIKOMO_CHA_VITABU}." ) k.aliyeazima = m.namba k.tarehe_ya_kuazima = (leo or date.today()).isoformat() m.vitabu.append(k.isbn) @hifadhi_baadaye def rudisha(self, isbn: str, leo: date | None = None) -> int: """Inarudisha faini (TSh) kama kitabu kimechelewa, vinginevyo 0.""" k = self.kitabu(isbn) if not k.kimeazimwa: raise HakijaAzimwa(f"'{k.kichwa}' hakijaazimwa.") leo = leo or date.today() siku = (leo - date.fromisoformat(k.tarehe_ya_kuazima)).days faini = max(0, siku - SIKU_ZA_KUAZIMA) * FAINI_KWA_SIKU m = self.mwanachama(k.aliyeazima) m.vitabu.remove(k.isbn) k.aliyeazima = None k.tarehe_ya_kuazima = None return faini # ---------- dunders ---------- def __len__(self) -> int: return len(self._vitabu) def __contains__(self, isbn: str) -> bool: return isbn in self._vitabu def __str__(self) -> str: return (f"Maktaba: vitabu {len(self)}, vinavyopatikana " f"{len(self.vinavyopatikana())}, wanachama {len(self._wanachama)}")
Maelezo ya kusema ukiandika:
Kitabu(**d)— dict{"isbn": "1", "kichwa": ...}inafunguliwa kuwa keyword arguments. Hii ndiyo sababu keys za JSON lazima zilingane na majina ya fields.@hifadhi_baadaye— decorator yetu wenyewe! Kila mabadiliko yanahifadhiwa yenyewe; tukisahau kuitahifadhi()hakuna tatizo. Kama method inarusha exception,self.hifadhi()haifikiwi — data mbaya haihifadhiwi.vilivyochelewani generator (yield).- Parameter
leoinaturuhusu "kujifanya" ni tarehe nyingine ili kujaribu faini (testing).
Jaribio la haraka — jaribio.py (si sehemu ya mradi, unaweza kuifuta baadaye):
from datetime import date from pathlib import Path from maktaba import Maktaba from makosa import KosaLaMaktaba from models import Kitabu, Mwanachama path = Path(__file__).parent / "jaribio.json" path.unlink(missing_ok=True) # anza upya kila mara m = Maktaba.pakia(path) m.ongeza_kitabu(Kitabu("978-1", "Kinjeketile", "Ebrahim Hussein", 1969)) m.ongeza_kitabu(Kitabu("978-2", "Rosa Mistika", "Euphrase Kezilahabi", 1971)) m.ongeza_kitabu(Kitabu("978-3", "Siku ya Watenzi Wote", "Shaaban Robert", 1968)) m.sajili(Mwanachama("M001", "Asha")) m.azima("978-1", "M001", leo=date(2026, 12, 1)) print(m) try: m.azima("978-1", "M001") except KosaLaMaktaba as e: print("Kosa:", e) for k, siku in m.vilivyochelewa(leo=date(2026, 12, 24)): print(f"Kimechelewa: {k.kichwa} siku {siku}") faini = m.rudisha("978-1", leo=date(2026, 12, 24)) print("Faini:", faini) m2 = Maktaba.pakia(path) # pakia upya kutoka JSON print(m2, "| '978-2' in m2:", "978-2" in m2) print([k.kichwa for k in m2.vitabu_vilivyopangwa("mwaka")]) print([str(k) for k in m2.tafuta("robert")])
Output:
Maktaba: vitabu 3, vinavyopatikana 2, wanachama 1 Kosa: 'Kinjeketile' kimeshaazimwa na M001. Kimechelewa: Kinjeketile siku 9 Faini: 1800 Maktaba: vitabu 3, vinavyopatikana 3, wanachama 1 | '978-2' in m2: True ['Siku ya Watenzi Wote', 'Kinjeketile', 'Rosa Mistika'] ['[978-3] Siku ya Watenzi Wote - Shaaban Robert (1968) | kipo']
Hesabu ya faini: Des 1 → Des 24 = siku 23; 23 − 14 = 9 × 200 = 1,800. Fungua jaribio.json uonyeshe data. Ongeza jaribio.json kwenye .gitignore (au futa file). Commit:
git add . git commit -m "Ongeza class Maktaba na kuhifadhi JSON"
Hatua 5: main.py
"""Mfumo wa Maktaba - menu ya terminal.""" from pathlib import Path from makosa import KosaLaMaktaba from maktaba import Maktaba from models import Kitabu, Mwanachama DATA = Path(__file__).parent / "data.json" MENU = """ ========= MFUMO WA MAKTABA ========= 1. Ongeza kitabu 6. Rudisha kitabu 2. Sajili mwanachama 7. Tafuta kitabu 3. Onyesha vitabu 8. Vitabu vilivyochelewa 4. Onyesha wanachama 9. Takwimu 5. Azima kitabu 0. Toka """ def soma_namba(ujumbe: str) -> int: while True: try: return int(input(ujumbe)) except ValueError: print("Tafadhali andika namba.") def onyesha(vitu) -> None: vitu = list(vitu) if not vitu: print("(hakuna)") for kitu in vitu: print(" ", kitu) def main() -> None: maktaba = Maktaba.pakia(DATA) print(maktaba) while True: print(MENU) chaguo = input("Chagua: ").strip() try: if chaguo == "1": kitabu = Kitabu( isbn=input("ISBN: ").strip(), kichwa=input("Kichwa: ").strip(), mwandishi=input("Mwandishi: ").strip(), mwaka=soma_namba("Mwaka: "), ) maktaba.ongeza_kitabu(kitabu) print("Kitabu kimeongezwa.") elif chaguo == "2": m = Mwanachama(input("Namba (mf. M001): ").strip(), input("Jina: ").strip()) maktaba.sajili(m) print("Mwanachama amesajiliwa.") elif chaguo == "3": onyesha(maktaba.vitabu_vilivyopangwa()) elif chaguo == "4": onyesha(maktaba._wanachama.values()) elif chaguo == "5": maktaba.azima(input("ISBN: ").strip(), input("Namba ya mwanachama: ").strip()) print("Kimeazimwa. Kirudishwe ndani ya siku 14.") elif chaguo == "6": faini = maktaba.rudisha(input("ISBN: ").strip()) print("Kimerudishwa." + (f" Faini: TSh {faini:,}" if faini else "")) elif chaguo == "7": onyesha(maktaba.tafuta(input("Neno la kutafuta: "))) elif chaguo == "8": onyesha(f"{k.kichwa} - siku {s}" for k, s in maktaba.vilivyochelewa()) elif chaguo == "9": print(maktaba) elif chaguo == "0": print("Kwaheri! Data imehifadhiwa.") break else: print("Chaguo si sahihi.") except KosaLaMaktaba as e: print("KOSA:", e) if __name__ == "__main__": main()
Endesha python main.py; ongeza vitabu 2, mwanachama 1, azima, jaribu kuazima tena (kosa linaonyeshwa vizuri, programu haifi), toka, endesha tena — data bado ipo!
Kumbuka: maktaba._wanachama.values() kwenye chaguo 4 ni kugusa attribute ya "ndani" — uliza mwanafunzi: "Hii inavunja kanuni gani ya Wiki 6? Tungeirekebishaje?" (Jibu: encapsulation; ongeza method wanachama() kwenye Maktaba inayorudisha list.) Ni zoezi zuri la likizo.
Hatua 6: README.md na push
# Mfumo wa Maktaba Mradi wa Mwezi 2 wa kozi ya Python. Programu ya terminal ya kusimamia vitabu, wanachama na kuazima, ikihifadhi data kwenye JSON. ## Jinsi ya kuendesha ``` python main.py ``` Inahitaji Python 3.12+. Hakuna packages za nje. ## Nilichojifunza - OOP: dataclasses, encapsulation, custom exceptions - Kuhifadhi data kwa JSON na pathlib - Comprehensions, lambda, generators na decorators - Git & GitHub
git add . git status # hakikisha data.json na .venv HAZIMO git commit -m "Ongeza menu ya main.py na README"
Kwenye GitHub: New repository → maktaba → Public → bila README → Create:
git remote add origin https://github.com/JINA_LAKO/maktaba.git git branch -M main git push -u origin main
Fungua repo kwenye browser — README inaonekana chini ya files. Hongera! Hii ni kazi ya kwanza kwenye portfolio ya mwanafunzi.
Maswali ya kumuuliza mwanafunzi
- Kwa nini tumegawa code kwenye files 4? — Kila file lina jukumu moja; ni rahisi kusoma, kujaribu na kubadilisha.
Kitabu(**d)inafanya nini? — Inafungua dict kuwa keyword arguments za__init__ya dataclass.- Decorator
hifadhi_baadayeinatusaidiaje? — Inahifadhi data yenyewe baada ya kila mabadiliko yaliyofanikiwa. - Kwa nini
except KosaLaMaktabamoja inatosha kwenyemain.py? — Makosa yote ya maktaba yanarithi kutoka kwake. - Kwa nini
data.jsoniko kwenye.gitignore? — Ni data ya watumiaji, si code. - Faini ya kitabu kilichoazimwa Des 1 na kurudishwa Des 20 ni ngapi? — Siku 19 − 14 = 5 × 200 = TSh 1,000.
- Ukifunga programu na kuifungua tena, data inatoka wapi? —
Maktaba.pakia(DATA)inasomadata.json.
Makosa ya kawaida
| Kosa | Ujumbe wa error | Suluhisho |
|---|---|---|
Kuendesha main.py kutoka folder jingine |
ModuleNotFoundError: No module named 'makosa' |
cd maktaba kwanza, kisha python main.py |
Kuita file maktaba.py na folder maktaba kisha kufanya import ndani ya folder la juu |
ImportError / module isiyo sahihi |
Endesha ukiwa ndani ya folder la mradi. |
| JSON ina key isiyo field (mf. ulibadilisha jina la field) | TypeError: Kitabu.__init__() got an unexpected keyword argument 'title' |
Futa data.json ya zamani au rekebisha keys. |
Import ya mzunguko (circular): models.py ina-import maktaba.py na kinyume |
ImportError: cannot import name 'Kitabu' from partially initialized module 'models' |
models.py isi-import maktaba.py; mtiririko uwe mmoja: main → maktaba → models/makosa. |
mwaka imeingizwa kama string |
Kupanga kwa mwaka kunaleta TypeError: '<' not supported between instances of 'str' and 'int' |
Tumia soma_namba() |
data.json imepakiwa GitHub |
— | git rm --cached data.json, ongeza kwenye .gitignore, commit. |
| Push inakataliwa (repo ina README) | ! [rejected] main -> main (fetch first) |
git pull origin main --allow-unrelated-histories, kisha git push |
Zoezi darasani
(Fanya kadri muda unavyoruhusu; yaliyobaki ni kazi ya likizo.)
Zoezi 3.1: Ongeza method wanachama(self) -> list[Mwanachama] kwenye Maktaba inayorudisha wanachama waliopangwa kwa jina, na itumie kwenye main.py badala ya maktaba._wanachama.
Zoezi 3.2: Ongeza chaguo "Futa kitabu" — kisiweze kufutwa kama kimeazimwa (rusha KimeshaAzimwa).
Zoezi 3.3: Ongeza takwimu kwenye chaguo 9: mwandishi mwenye vitabu vingi zaidi (dict comprehension / max(key=...)).
Jibu
# 3.1 -- ndani ya class Maktaba def wanachama(self) -> list[Mwanachama]: return sorted(self._wanachama.values(), key=lambda m: m.jina.lower()) # main.py, chaguo 4: # onyesha(maktaba.wanachama()) # 3.2 -- ndani ya class Maktaba @hifadhi_baadaye def futa_kitabu(self, isbn: str) -> Kitabu: k = self.kitabu(isbn) if k.kimeazimwa: raise KimeshaAzimwa(f"Huwezi kufuta '{k.kichwa}' - kimeazimwa.") return self._vitabu.pop(isbn) # main.py: # elif chaguo == "10": # k = maktaba.futa_kitabu(input("ISBN: ").strip()) # print(f"Kimefutwa: {k.kichwa}") # (na ongeza "10. Futa kitabu" kwenye MENU) # 3.3 -- ndani ya class Maktaba def mwandishi_bora(self) -> tuple[str, int] | None: if not self._vitabu: return None waandishi = {k.mwandishi for k in self._vitabu.values()} hesabu = { w: sum(1 for k in self._vitabu.values() if k.mwandishi == w) for w in waandishi } jina = max(hesabu, key=hesabu.get) return jina, hesabu[jina] # main.py, chaguo 9: # print(maktaba) # bora = maktaba.mwandishi_bora() # if bora: # print(f"Mwandishi mwenye vitabu vingi: {bora[0]} ({bora[1]})")
Kumbuka kufanya git add ., git commit -m "...", git push baada ya kila zoezi.
Tathmini ya Mwezi 2
Muulize mwanafunzi (kwa mdomo) aeleze kwa maneno yake: (1) class vs object, (2) try/except, (3) kwa nini venv, (4) decorator ni nini, (5) git add → commit → push. Andika kwenye daftari lako mada alizoshindwa kueleza — zitarudiwa Wiki 9.
Kazi ya Nyumbani
(Kazi 1 inatakiwa Kipindi 2. Kazi 2 inatakiwa Kipindi 3. Kazi ya likizo — tazama hapa chini — inatakiwa: Kipindi 1 cha Wiki 9.)
Kazi 1 — Comprehensions (baada ya Kipindi 1): Kutoka list ya dicts ya wanafunzi [{"jina": ..., "alama": [..]}, ...] (angalau 5): (a) dict ya jina: wastani kwa dict comprehension, (b) majina ya waliopata wastani ≥ 60 yaliyopangwa kutoka juu, (c) set ya alama zote za kipekee.
Kazi 2 — Maandalizi ya mradi (baada ya Kipindi 2): (a) Generator soma_csv(path) inayotoa kila row ya CSV kama dict (tumia csv.DictReader). (b) Decorator thibitisha_chanya inayorusha ValueError kama argument yoyote ya namba ni hasi. (c) Hakikisha Git na akaunti ya GitHub ziko tayari, na repo python-kozi ina angalau commit 1.
Wiki ya Likizo (hakuna vipindi)
Mwambie mwanafunzi: "Pumzika, sherehekea sikukuu! Kama una muda, haya ni mazoezi ya hiari."
- Malizia mazoezi 3.1–3.3 ya mradi wa maktaba na push GitHub.
- Ongeza kwenye maktaba: kuhifadhi historia ya kila kuazima (list ya dicts kwenye JSON) na chaguo la kuionyesha.
- Rudia Wiki 5–8: andika upya bila kuangalia
BankAccount(Wiki 6) kama@dataclassyenye@property. - Soma README za repos 2 za Python kwenye GitHub (mf. https://github.com/TheAlgorithms/Python) — angalia jinsi wanavyopanga files.
- Swali la kufikiri: generator
vilivyochelewaingeweza kuandikwa kama list comprehension? Faida/hasara ni zipi?
Jibu
# Kazi 1 wanafunzi = [ {"jina": "Asha", "alama": [80, 90, 85]}, {"jina": "Juma", "alama": [40, 55, 50]}, {"jina": "Neema", "alama": [70, 65, 75]}, {"jina": "Baraka", "alama": [60, 58, 62]}, {"jina": "Zawadi", "alama": [90, 95, 100]}, ] wastani = {w["jina"]: sum(w["alama"]) / len(w["alama"]) for w in wanafunzi} print(wastani) # {'Asha': 85.0, 'Juma': 48.33..., 'Neema': 70.0, 'Baraka': 60.0, 'Zawadi': 95.0} waliofaulu = sorted((j for j, a in wastani.items() if a >= 60), key=lambda j: wastani[j], reverse=True) print(waliofaulu) # ['Zawadi', 'Asha', 'Neema', 'Baraka'] kipekee = {a for w in wanafunzi for a in w["alama"]} print(sorted(kipekee))
# Kazi 2 import csv import functools def soma_csv(path): with open(path, newline="", encoding="utf-8") as f: for row in csv.DictReader(f): yield row def thibitisha_chanya(func): @functools.wraps(func) def wrapper(*args, **kwargs): for thamani in list(args) + list(kwargs.values()): if isinstance(thamani, (int, float)) and thamani < 0: raise ValueError(f"{func.__name__}: thamani hasi hairuhusiwi ({thamani})") return func(*args, **kwargs) return wrapper @thibitisha_chanya def eneo_la_mstatili(urefu, upana): return urefu * upana print(eneo_la_mstatili(3, 4)) # 12 try: eneo_la_mstatili(3, upana=-1) except ValueError as e: print(e) # eneo_la_mstatili: thamani hasi hairuhusiwi (-1)
Likizo, swali 5: Ndiyo — [(k, s) for ...] ingewezekana, lakini ingehitaji kuhesabu siku mara mbili au kutumia walrus operator, na ingejenga list nzima. Generator ni safi zaidi kusoma na inatumia memory kidogo; hasara ni kwamba inaweza kuzungukwa mara moja tu.
Ukiulizwa swali usilolijua
- Git ndiyo sehemu yenye maswali mengi yasiyotarajiwa. Kanuni: soma ujumbe wa
git status— mara nyingi unasema cha kufanya. Usitumiegit push --forcewalagit reset --harddarasani bila kuelewa — vinaweza kufuta kazi. Ukikwama, nakili ujumbe wa kosa na utafute neno kwa neno. - Docs: comprehensions https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions | sorting HOWTO https://docs.python.org/3/howto/sorting.html | generators https://docs.python.org/3/howto/functional.html |
functools.wrapshttps://docs.python.org/3/library/functools.html#functools.wraps | Git book https://git-scm.com/book/en/v2 | GitHub docs https://docs.github.com/en/get-started |.gitignoretemplates https://github.com/github/gitignore/blob/main/Python.gitignore - Sema: "Hata developers wakubwa wanatafuta amri za Git kila siku — kujua kutafuta ni ujuzi."
Checklist ya mwisho wa wiki
- Anaandika list/dict/set comprehensions pamoja na
ifnaif/else. - Anatumia
lambdanasorted(key=...),max(key=...). - Anajua
map/filterzinarudisha iterator (anatumialist()). - Anaeleza
iter/next/StopIterationna anaandika generator kwayield. - Anaandika decorator yenye
*args, **kwargsnafunctools.wraps. - Ameweka
git config(jina, email) na anatumiainit,status,add,commit,log. - Ana
.gitignoreyenye.venv/na__pycache__/. - Amepush repo kwenye GitHub bila msaada.
- Mradi wa "Mfumo wa Maktaba" unafanya kazi (ongeza, sajili, azima, rudisha, tafuta, JSON inakumbuka data).
- Mradi uko GitHub ukiwa na README.
- Anajua kazi za hiari za wiki ya likizo.