Paano tumatakbo ang Python
Ang Python ay nagbabasa ng code mo mula itaas pababa, isang line sa isang pagkakataon. Parang recipe: step 1 muna bago step 2. Ang print() ay nagpapakita ng kahit ano sa screen (output). Ang mga line na nagsisimula sa # ay comment: notes mo lang 'yan para sa sarili mo, hindi pinapatakbo ng Python.
Ang text ay nakapaloob sa quotes ("..." o '...'). Ang print(2 + 3) ay magpapakita ng 5, pero ang print("2 + 3") ay literal na 2 + 3, kasi text na siya.
Variables: mga name tag
Isipin mo ang variable bilang name tag na idinidikit sa isang value. Kapag sinabi mong edad = 18, gumawa ang Python ng value na 18 at idinikit ang tag na edad doon.
Importante: ang = sa Python ay hindi "equals" na parang sa math. Ang ibig sabihin niya ay "i-assign": kunin ang value sa kanan, idikit sa pangalan sa kaliwa. Kaya valid ang edad = edad + 1 (sa math, imposible 'yan!).
- Letters, numbers, at
_lang. Bawal mag-umpisa sa number. - Case-sensitive: magkaiba ang
Edadatedad. - Gumamit ng
snake_case:final_grade, hindifinalgrade.
Try mo: ano ang output?
x = 10
y = x
x = 99
print(y)
Sagot: 10. Noong y = x, idinikit ang tag na y sa value na 10. Nang ilipat ang x sa 99, hindi gumalaw si y.
Data types: anong klase ng value?
Bawat value sa Python ay may type. Ito ang nagsasabi kung ano ang pwede mong gawin sa kanya: pwedeng i-add ang dalawang number, pero ang dalawang text ay pinagdudugtong. Para malaman ang type, gamitin ang type().
| Type | Para saan | Halimbawa |
|---|---|---|
| intinteger | Whole numbers, walang decimal | 19, -5, 2026 |
| floatdecimal | Numbers na may decimal point | 1.75, 3.0, -0.5 |
| strstring | Text, laging naka-quotes | "Ana", 'BSIT', "19" |
| boolboolean | Oo o hindi lang: True o False | True, False |
Mag-ingat sa "19" at "True": dahil naka-quotes, str sila, hindi number o bool.
Type conversion
Ang input() ay laging nagbibigay ng str, kahit number ang tinype ng user. Kaya kailangan mong i-convert:
edad = input("Edad mo? ") # "18" (str)
edad = int(edad) # 18 (int)
print(edad + 1) # 19
Bakit nag-e-error?
print("5" + 5)
Hindi alam ng Python kung pagdudugtungin ba (text) o i-a-add (math). Gawin mong pareho ang type: int("5") + 5 → 10, o "5" + str(5) → "55".
Strings: text na naka-hilera
Ang string ay parang hilera ng mga kahon, isang letter bawat kahon. Bawat kahon may index (address). Nagsisimula ang bilang sa 0, hindi sa 1. Pwede ka ring magbilang mula sa dulo gamit ang negative index: -1 ang huling letter.
Pwede ka ring mag-click ng isang kahon para makita ang salita[i] nito.
salita[start:stop] ay kukunin mula sa start hanggang bago mag-stop. Kasama ang start, hindi kasama ang stop. Kaya ang salita[0:3] ay 3 letters: index 0, 1, at 2.
s = "Python"
print(len(s)) # 6
print(s.upper()) # PYTHON
print(s.lower()) # python
print(s[::-1]) # nohtyP (baliktad!)
print("th" in s) # True
name = "Ana"
yr = 1
# f-string: ilagay ang variable sa {}
print(f"Hi {name}, year {yr} ka na!")
# Hi Ana, year 1 ka na!
Lists: isang variable, maraming laman
Ang list ay naka-ayos na koleksyon ng values sa loob ng [ ], hiwalay sa comma. Parang strings din: may index na nagsisimula sa 0. Ang pagkakaiba, pwede mong baguhin ang laman ng list: magdagdag, magtanggal, mag-ayos.
grades = [88, 92, 75]
print(grades[0]) # 88 (una)
print(grades[-1]) # 75 (huli)
grades[1] = 95 # palitan ang index 1
print(grades) # [88, 95, 75]
print(sum(grades)) # 258
Ang .append(x) ay nagdadagdag sa dulo. Ang .pop() ay nagtatanggal ng huli at ibinabalik ito. Kapag walang laman ang list at nag-pop ka, error 'yan.
Dictionaries: hanapin gamit ang pangalan
Sa list, hinahanap mo ang laman gamit ang number (index). Sa dictionary, hinahanap mo gamit ang key, parang student record: key → value. Nakasulat ito sa { }.
student = {"name": "Ana", "course": "BSCS", "year": 1, "scholar": True}
Gamitin ang list kapag sunod-sunod na items na magkakapareho ng klase (listahan ng grades). Gamitin ang dict kapag bawat value ay may sariling label (isang student na may name, course, year).
if / elif / else: pagdedesisyon
Ang if ay nagtatanong ng tanong na True o False ang sagot. Kapag True, tatakbo ang block sa ilalim niya. Ang mga elif ay chine-check isa-isa, mula taas pababa, at titigil sa unang True. Kapag walang True, ang else ang tatakbo.
Comparison operators
== | magkapareho ba? |
!= | magkaiba ba? |
> < | mas malaki / mas maliit |
>= <= | mas malaki o equal / mas maliit o equal |
and or not | pagsamahin ang mga kondisyon |
Sa ibang language, gumagamit ng { }. Sa Python, ang indent (4 spaces) ang nagsasabi kung aling code ang kasama sa if. Huwag kalimutan ang colon : sa dulo ng if, elif, else, for, while, at def.
At tandaan: = ay assign, == ay compare.
Loops: ulit-ulitin nang hindi kinokopya
Ang for loop ay dumadaan sa bawat item ng isang list (o string, o range), isa-isa. Sa bawat ikot, ang loop variable ay idinidikit sa susunod na item.
Kapag gusto mo lang umulit ng ilang beses, gamitin ang range(). Gumagawa ito ng sunod-sunod na numbers. Pareho ang rule sa slicing: hindi kasama ang stop.
while loop
Uulit habang True ang kondisyon. Siguraduhing may nagbabago sa loob, kung hindi, infinite loop (hindi titigil; pindutin ang Ctrl + C para ihinto).
tulog = 3
while tulog > 0:
print("5 minutes pa...")
tulog = tulog - 1
print("Gising na!")
break at continue
for n in range(1, 10):
if n == 3:
continue # skip si 3
if n == 6:
break # tigil na sa 6
print(n)
# 1 2 4 5
Functions: recipe na pwedeng ulit-ulitin
Ang function ay grupo ng code na may pangalan. Isusulat mo minsan gamit ang def, tapos tatawagin mo kahit ilang beses. Pumapasok ang data sa parameters, at lumalabas ang sagot gamit ang return.
Ang print() ay nagpapakita lang sa screen. Ang return ay nagbabalik ng value para magamit mo pa (i-save sa variable, i-compute). Kung walang return, ang ibinabalik ng function ay None.
def greet(name, course="BSCS"):
return f"Hi {name} ng {course}!"
print(greet("Ana")) # default course
print(greet("Ben", "BSIT")) # may sariling course
Error decoder: huwag matakot sa pula
Normal ang errors, kahit sa mga senior dev. Ang error message ay clue, hindi insulto. Basahin mula sa pinakababa: doon nakasulat ang type ng error at ang line number.
if edad > 18
print("Adult")
Mali ang "grammar" ng code. Dito, kulang ang colon : pagkatapos ng if. Check din ang kulang na quotes o parenthesis.
for x in range(3):
print(x)
Hindi naka-indent ang code na dapat nasa loob ng block. Lagyan ng 4 spaces ang print(x).
pangalan = "Ana"
print(Pangalan)
Hindi kilala ng Python ang pangalan. Kadalasan typo o maling capitalization. pangalan ≠ Pangalan.
edad = input("Edad? ")
print(edad + 1)
Pinaghalo ang magkaibang type. Ang input() ay str, kaya i-int() mo muna bago i-add.
nums = [10, 20, 30]
print(nums[3])
Lampas sa dulo ng list. 3 items = index 0, 1, 2 lang. Ang huli ay nums[2] o nums[-1].
grades = []
print(sum(grades) / len(grades))
Nag-divide sa zero. Walang laman ang list kaya len() ay 0. Mag-check muna: if len(grades) > 0:
Quick quiz
Anim na tanong lang. Pumili ng sagot para makita agad kung tama, at bakit.
Ang scientific calculator mo: patakbuhin, gamitin, ipaliwanag
Oras na para sa totoong program: ang scientific_calculator.py. Nagbubukas siya ng window na may 53 keys, ipinapakita niya ang sagot habang nagta-type ka pa lang, at may history siya ng mga na-compute mo. Mga 1,300 lines siya, pero gawa siya sa mga natutunan mo na: strings, lists, dicts, tuples, if/elif, loops at functions, plus tatlong bagong idea na makikilala mo na rin soon: classes, exceptions at recursion.
Pagkatapos ng lesson na 'to, dapat kaya mo na siyang patakbuhin sa sarili mong computer, gamitin ang bawat key, at ipaliwanag sa iba kung paano siya gumagana.
APatakbuhin sa computer mo
- Mag-install ng Python 3.
I-download sa python.org. Sa Windows, i-tick ang “Add python.exe to PATH” sa unang screen ng installer. Tkinter ang gamit ng calculator para gumawa ng window, at kasama na 'yan sa Python, kaya wala ka nang ibang kailangang i-install.
- Ilagay ang dalawang file sa iisang folder.
Halimbawa sa
Documents\calculator, at nasa loob angscientific_calculator.py(ang app) attest_scientific_calculator.py(ang tests niya). - Magbukas ng terminal sa folder na 'yon.
Sa Windows: buksan ang folder sa File Explorer, i-click ang address bar, i-type ang
cmd, tapos Enter. Sa VS Code: File → Open Folder…, tapos Terminal → New Terminal. - Patakbuhin ang calculator.
> python scientific_calculator.pyLalabas ang window. Isara mo lang siya gaya ng ibang window kapag tapos ka na.
- Patakbuhin ang tests (optional, pero ang sarap sa feeling).
> python -m unittest -v test_ans_holds_last_result (…EngineTest…) ... ok test_arithmetic_and_precedence (…EngineTest…) ... ok … 41 pa … ---------------------------------------------------------------------- Ran 43 tests in 4.2s OK
Isang test ang bawat line. Kapag
OKsa dulo, ibig sabihin tama lahat ng sagot na chineck ng tests.
Kapag may problema
| Nakikita mo… | Ibig sabihin… | Gawin mo 'to |
|---|---|---|
'python' is not recognized…, o bumukas ang Microsoft Store | Hindi pa naka-install ang Python, o hindi siya mahanap ng Windows. | Subukan ang py scientific_calculator.py. Kapag ayaw pa rin, patakbuhin ulit ang Python installer at i-tick ang “Add python.exe to PATH”. |
can't open file … No such file or directory | Nasa ibang folder ang terminal. | I-type ang dir (Windows) o ls (Mac) para makita ang mga file doon. Gamitin ang cd para lumipat sa folder na may mga .py file. |
ModuleNotFoundError: No module named 'tkinter' | Na-install ang Python na 'to nang walang Tkinter. | Windows: patakbuhin ulit ang installer, piliin ang Modify, at i-tick ang “tcl/tk and IDLE”. Linux: sudo apt install python3-tk. |
FAILED (failures=1) pagkatapos ng tests | May binago sa code na sumira sa isang sagot. | Basahin ang pangalan at input ng test na nag-fail: sinasabi niya kung ano mismo ang nasira. I-undo ang huling binago mo at patakbuhin ulit ang tests. |
BGamitin: tour ng window
1
2
3
4
5
6
7
8
9
10
- 1Status chips. Ang
DEGoRADay ang angle mode. Lumalabas angM 3.14159habang may naka-save sa memory. - 2Ipakita / itago ang history. Lumiliit ang window kapag nakatago.
- 3Huling na-compute, tulad ng
2π×sin(30) =. Dito rin lumalabas nang pula ang mga error. - 4Ang display. Dito ka mag-type o gamitin ang mga key. Pwede mong i-click sa loob para i-edit ang kahit anong parte. Lumiliit ang font kapag mahaba na.
- 5Live preview. Lumalabas na ang sagot habang nagta-type ka, bago mo pa pindutin ang =.
- 6Scientific keys: trig, powers, roots, logs at constants.
- 7Memory keys para mag-save ng number para mamaya.
- 8Number pad kasama ang apat na operator, brackets, clear at backspace.
- 9Equals: kino-compute ang sagot.
- 10History. Naka-save dito ang bawat sagot, pinakabago sa itaas. I-click ang isa para gamitin ulit.
Ano ang ginagawa ng bawat key
| Keys | Ginagawa nila | Subukan |
|---|---|---|
| 0–9.+ − × ÷( ) | Normal na math. Brackets muna, tapos × at ÷, tapos + at −. | 2+3×4 → 14 |
| = | Nagko-compute. Kapag pinindot ulit, walang magbabago. | |
| AC⌫ | Binubura ng AC ang lahat. Isang character lang ang binubura ng ⌫. | |
| ± | Binabaliktad ang sign ng huling number (3+5 → 3−5). Kapag kakapindot mo lang ng =, ang sagot ang binabaliktad. | |
| sincostan | Mga trig function. Ang kabaligtaran nila ay sin⁻¹ cos⁻¹ tan⁻¹. | sin(30) → 0.5 |
| DEG | Nagpapalit sa degrees (DEG) at radians (RAD). Ipinapakita ng chip ① kung alin ang naka-on. | |
| sinhcoshtanh | Mga hyperbolic function (makikilala mo sila sa calculus). | cosh(0) → 1 |
| x²x³xʸ | Square, cube, kahit anong power. Ang xʸ ay nagta-type ng ^. | 2^10 → 1024 |
| √∛ | Square root at cube root. | √(144) → 12 |
| 10ˣeˣlogln | Powers ng 10 at ng e, at ang kabaligtaran nila: base 10 ang log, base e ang ln. | log(1000) → 3 |
| 1/x | Isa na hinati sa number (reciprocal). | 4⁻¹ → 0.25 |
| x! | Factorial: 5! = 5×4×3×2×1. Buong number lang mula 0 hanggang 170. | 5! → 120 |
| % | Percent: hinahati sa 100. | 200×10% → 20 |
| mod | Ang natira (remainder) pagkatapos mag-divide. | 7 mod 3 → 1 |
| |x| | Absolute value: tinatanggal ang minus sign. | abs(−5) → 5 |
| πe | Ang constants na π = 3.14159… at e = 2.71828… | 2π → 6.28318530718 |
| EXP | Scientific notation. Nagta-type ng malaking E: ang 6.02E23 ay 6.02 × 10²³. | 1.5E3 → 1500 |
| Ans | Ang huling sagot. | |
| MCMRM+M− | Memory. Idinadagdag ng M+ ang sagot sa memory, ibinabawas ng M−, tina-type ng MR, at binubura ng MC. |
Mga keyboard shortcut
| Pindutin | Mangyayari |
|---|---|
| 0–9 + ( ) . ^ ! % | Tina-type sila. Umiilaw ang katapat na key sa screen. |
| * / - | Nagiging × ÷ −. |
| Mga letra, tulad ng sin( | Pwede mong i-type nang direkta ang pangalan ng function. Ang malaking E ay pareho ng EXP. |
| Enter o = | Nagko-compute. |
| Esc / Backspace | Binubura lahat / binubura ang isang character. |
| ↑ ↓ | Ibinabalik ang mga naunang na-compute, gaya sa terminal. |
| Ctrl+V | Nagpe-paste ng expression. |
- Kapag operator ang pinindot mo pagkatapos ng =, tuloy lang mula sa sagot. Ipinapakita ng display ang
Ans×…, at buo pa rin ang Ans, hindi lang ang digits na nakikita mo sa screen. - Kapag number ang pinindot mo pagkatapos ng =, panibagong calculation na.
- Hindi mo kailangang isara ang brackets: gumagana ang
sin(30. - Pwede mong hindi isulat ang ×: gumagana ang
2π,3sin(30)at2(3+4). - Ang maliit na
eay ang number na e. Ang malakingE(EXP) ay scientific notation. - Hanggang 12 digits ang ipinapakita ng display, kaya
0.3ang lalabas sa0.1+0.2. - Hindi nagka-crash ang app kapag may pulang error. Ayusin lang ang expression at pindutin ulit ang =.
Subukan: anim na maliit na problema (nasa loob ang sagot)
- Ang pinakamahabang side ng right triangle na may sides na 3 at 4:
√(3²+4²)→ 5 - ₱1,000 na naka-ipon nang may 5% kada taon sa loob ng 10 taon:
1000×1.05^10→ 1628.89462678 - I-check ang sikat na rule:
sin(37)²+cos(37)²→ 1 (subukan kahit anong angle) - Area ng bilog na radius 7:
π×7²→ 153.938040026 - Ilang paraan para pagsunud-sunurin ang 5 libro:
5!→ 120 - Ilang minuto sa isang taon:
365×24, pindutin ang =, tapos×60(makikita mo sa display angAns×60) → 525600
CPaano siya gumagana
Ang big picture
Ang window
Ipinapakita ng CalculatorApp ang mga key at ang display. Kapag pinindot mo ang =, ipinapadala niya ang text, tulad ng "3sin(30", sa engine.
Ang engine
Kino-compute ng ScientificEngine.evaluate() ang sagot sa dalawang step:
- Hinahati ng
_tokenizeang text sa maliliit na piraso (tokens). - Binabasa ng
_Parserang mga piraso sa tamang pagkakasunod at nagko-compute.
o error
Balik sa window
Ginagawang text ng format_number() ang 1.5, para maging "1.5". Ipinapakita 'yon ng window at idinadagdag sa history.
Hindi kailanman ginagalaw ng engine ang window. Kaya kayang i-check ng tests ang mahigit isandaang sagot sa ilang segundo nang hindi binubuksan ang window, at kaya rin pwedeng gamitin ulit ang parehong engine sa website. (Ginagawa 'yan ng page na 'to: JavaScript copy ng engine ang tumatakbo sa lab sa ibaba.)
Mapa ng file
import, at CalculatorError, ang sariling error type ng app× → *), mga kilalang pangalan tulad ng sin at pi, at _tokenize()_Parser: binabasa ang tokens sa tamang pagkakasunod at kino-compute ang sagotmod, factorial, at paglilinis ng maliliit na rounding errorScientificEngine: degrees o radians, Ans, friendly na error messagesformat_number(), complete_parentheses(), toggle_sign(): maliliit na text helperTHEME colors, KEY_STYLES, laki ng font ng display, ang app iconRoundedButton, Card, Key at KeypadCalculatorApp: ang window, keyboard, memory, live preview at historymain(): sinisimulan ang appSundan ang isang calculation
JavaScript copy ng engine sa .py file ang nandito, para tumakbo siya sa web page. Pareho ang steps, pareho ang sagot at error messages: chineck siya laban sa Python version gamit ang 40,000 random na expression. Pwede ka ring mag-type sa keyboard.
Paano niya nalalaman na × muna bago +?
Hagdan ng mga function ang parser. Bawat baitang ay may hawak na isang grupo ng operator, tapos hinihingi niya sa baitang sa ilalim ang mga piraso sa magkabilang side. Mas mahigpit ang kapit ng mas mababang baitang, kaya sila ang unang nako-compute. PEMDAS siya na isinulat bilang code.
def expr(self):
value = self.term()
while True:
if self._accept("+"):
value += self.term()
elif self._accept("-"):
value -= self.term()
else:
return value
Isa siyang while loop na may if / elif / else. Kumuha ng isang term. Habang + o - ang susunod na token, kumuha ng isa pang term at i-add o i-subtract. Kapag wala nang + o -, i-return ang value.
Ang brackets ang matalinong parte: tinatawag ulit ng primary ang expr para sa kung anuman ang nasa loob ng ( ). Ang function na nauuwi sa pagtawag sa sarili niya ay tinatawag na recursion.
Mga alam mo na, nasa loob ng calculator
Dict ng dicts at tuples
KEY_STYLES = {
"digit": {"faces": ("#252a37", "#2f3545", "#3a4153"),
"text": "#f3f5fa"},
"operator": {"faces": ("#2a2552", "#342e64", "#3e3777"),
"text": "#c9c1ff"},
...
}
face = style["faces"][STATES.index(state)]
Bawat klase ng key ay may tuple ng tatlong kulay: normal, nakatapat ang mouse, at pinindot. Ang STATES.index("hover") ay 1, kaya pangalawang kulay ang gamit ng key na nakatapatan ng mouse.
Dict ng mga function
_POSTFIX = {
"!": _factorial,
"%": lambda value: value / 100,
"²": lambda value: _power(value, 2.0),
...
}
value = _POSTFIX[text](value)
Pwedeng function ang value ng dict. Function ang makukuha sa _POSTFIX["%"], at tinatawag siya ng (value). Ang lambda value: value / 100 ay maliit na one-line function na walang pangalan. Ganito rin ang trick ng engine para sa "sqrt": math.sqrt at iba pa.
String methods
def complete_parentheses(expression):
return expression + ")" * max(
expression.count("(") - expression.count(")"), 0)
Para sa "sin(30": isang ( bawas zero na ) ay 1, at ang ")" * 1 ay ")", kaya "sin(30)" ang lalabas. Inuulit ang string kapag mini-multiply. Pinipigilan ng max(…, 0) na maging negative.
While loop sa isang string
tokens = []
pos = 0
while pos < len(text):
if text[pos].isspace():
pos += 1
continue
match = _TOKEN_RE.match(text, pos)
...
tokens.append((kind, match.group()))
pos = match.end()
Naglalakad ang tokenizer sa text gamit ang index na pos. Nilalaktawan niya ang spaces gamit ang continue, kumukuha ng isang number, pangalan o simbolo, ina-append sa list, at tinatalon ang pos lampas doon. (Pinaikli; tinatago ng ... ang mga error check.)
List bilang history
self.history.append((expression, value))
# ↑ key: umatras sa list
position = len(self.history) - 1
text = self.history[position][0]
Naka-save ang bawat sagot bilang tuple na (expression, value) sa dulo ng list. Nagsisimula ang ↑ sa huling index, len(…) - 1, at umaatras ng isa bawat pindot.
if / elif gamit ang strings
if head.endswith("+"):
return head[:-1] + "−" + number
if head.endswith(("−", "-")):
...
return head + "−" + number
Parte 'to ng ± key. Kapag nasa likod ng + ang number, palitan ng −. Slice ang head[:-1]: lahat maliban sa huling character.
Mga bagong idea na makikilala mo next
class ScientificEngine:
def __init__(self):
self.degrees = True
self.ans = 0.0
engine = ScientificEngine()
engine.evaluate("2+3") # 5.0, at 5.0 na rin ang engine.ans
Classes. Pinagsasama ng class ang data (degrees, ans) at ang mga function na gumagamit nito, na tinatawag na methods. Ang self ay "itong calculator na 'to mismo". Kapag tinawag mo ang ScientificEngine(), gagawa siya ng isa.
try:
result = _Parser(tokens, self).parse()
except ZeroDivisionError:
raise CalculatorError("Cannot divide by zero") from None
Exceptions. Pinapatakbo ng try ang code na pwedeng pumalya, at sinasalo ng except ang isang klase ng error. Dito, ang sariling ZeroDivisionError ng Python ay nagiging friendly na message imbes na mag-crash ang app. Subukan ang 1÷0 sa lab sa itaas.
self.bind("<ButtonRelease-1>", self._on_release)
self.entry.bind("<Return>", ...)
self.entry_var.trace_add("write", self._on_text_change)
...
CalculatorApp().mainloop()
Event-driven na program. Walang tumatakbo nang kusa. Hinihintay ng bawat function ang event niya: binitawan ang mouse button, pinindot ang Enter, o may nagbago sa text ng display. 'Yung huli ang dahilan kung bakit nag-a-update ang live preview sa bawat pindot. Naghihintay ang mainloop() ng mga event hanggang isara ang window.
Paano iginuguhit ang window
Plain na rectangle lang ang normal na button ng Tkinter, kaya ang calculator mismo ang gumuguhit ng mga key niya. Bawat key ay isang larawan ng rounded rectangle, plus ang text niya, at nakalagay ang 53 keys sa iisang malaking canvas. Galing sa kaunting geometry ang makinis na corners: para sa bawat pixel malapit sa corner, sukatin kung gaano siya kalayo sa gitna ng bilog sa corner. Kapag kalahati ng pixel ang nasa loob ng bilog, kalahating halo ng kulay ng key at ng background ang kulay niya.
def _coverage(distance, radius):
"""How much of a pixel lies inside the circle (0 to 1)."""
return min(max(radius - distance + 0.5, 0.0), 1.0)
Bakit iisang canvas? Noong una, kanya-kanyang widget ang bawat key, at parang lag kapag nire-resize ang window. Nang sinukat, lumabas ang dahilan: kailangang ilipat at i-repaint ng Tk ang 53 magkakahiwalay na widget sa bawat resize step. Naayos 'yon ng iisang canvas na may shared na larawan:
Sukatin muna, tapos ayusin ang mabagal na parte. Kung nanghula lang, Python code sana ang inayos, pero maliit na bahagi lang pala 'yon ng oras.
Tests: code na nagche-check ng code
def test_superscripts_are_postfix_operators(self):
self.assertResults({
"3²5": 45, # (3²)·5, not 3^25
"2²³": 64, # (2²)³
"3²!": 362880,
})
Sinasabi ng bawat line na "ang input na 'to ay dapat magbigay ng sagot na 'to". Kapag may binago na sumira sa calculator, magfe-fail ang test at sasabihin niya ang mismong input. May 43 tests ang file. Nasa ibaba ang engine at helper checks niya, tumatakbo mismo ngayon sa browser mo laban sa copy ng engine sa page na 'to.
Ipakita ang bawat check
| Test | Input | Inaasahan | Lumabas |
|---|
Mga bug na nahuli ng testing
Bago natapos ang calculator, nakahanap ng 12 bugs ang maingat na review plus 120,000 random na test input. Ilan sa kanila:
Ginawang text na ^2 ang ², kaya naging 3^25 ang 3²5. Ayos: naging sariling tokens ang ² ³ ⁻¹ sa postfix na baitang.
Dumikit sa 2 ang number galing sa memory. Ayos: kapag nasa likod mismo ng digit ang cursor, maglagay ng × sa gitna.
Naka-binary ang 0.1 sa loob ng computer, kaya 0.0999… ang lumabas na remainder. Ayos: kapag ang remainder ay sobrang lapit na sa 0 o sa divisor (rounding noise lang), 0 na siya.
Sa loob ng computer, 0.9999999999999999 talaga ang tan(45). Ayos: ang number na ganoon kalapit sa buong number ay itinuturing nang buo.
Naging bagong test ang bawat bug, kaya hindi na siya tahimik na makakabalik.
- Patakbuhin at laruin muna.
- Basahin ang maiikling helper:
format_numberatcomplete_parentheses. - Tapos ang
_tokenize, tapos ang parser, katabi ang hagdan sa itaas. - Magbago ng isang maliit na bagay (kulay sa
KEY_STYLES, bagong error message), patakbuhin ang tests, at tingnan kung ano ang mangyayari.
DIpaliwanag sa iba
Ang 30-second version
“Gumawa ako ng scientific calculator sa Python. Kapag nag-type ka ng tulad ng 3sin(30), ipinapakita na niya ang sagot habang nagta-type ka pa lang. Hindi siya gumagamit ng eval() ng Python. Hinahati niya ang text sa tokens, tapos binabasa ng parser ang mga 'yon sa tamang pagkakasunod, parang PEMDAS, para ma-compute ang sagot. Tkinter ang gamit sa window, at may 43 automatic tests na nagche-check kung tama ang math.”
Ang 2-minute demo
- Magbukas ng terminal at i-run ang
python scientific_calculator.py.Isang Python file lang siya, at ang gamit lang niya ay kung ano ang kasama na sa Python. - I-type ang
2+3×4. Ituro ang preview na= 14.Sinusunod niya ang order of operations, at ipinapakita na niya ang sagot bago ko pa pindutin ang equals. - Pindutin ang =, tapos i-type ang
×2.Kapag tinuloy ko pagkatapos ng equals, sa sagot siya nagpapatuloy, na tinatawag na Ans. - I-type ang
sin(30at pindutin ang =.Siya na ang nagsasara ng bracket. Degrees ang ibig sabihin ng DEG; itong button ang lumilipat sa radians. - I-type ang
1÷0at pindutin ang =.Message ang lumalabas kapag may error, hindi nagka-crash. Sa code, 'yan ang try at except. - I-click ang isang sagot sa history panel.Naka-save sa isang list ang bawat sagot, at kapag kinlick ko, magagamit ko ulit.
- Buksan ang code sa
_tokenize, tapos saexpr.Hinahati muna ang text sa tokens. Tapos ang parser ay parang hagdan ng mga function, isa sa bawat level ng order of operations. - I-run ang
python -m unittest -v.Chine-check ng 43 tests na 'to ang mahigit isandaang sagot sa ilang segundo, kaya alam kong walang nasira kapag may binago ako.
Mga posibleng itanong sa 'yo
Bakit hindi na lang gamitin ang eval() ng Python?
Pinapatakbo ng eval() ang kahit anong Python code, hindi lang math. Kapag may nag-type ng __import__('os').remove('notes.txt'), buburahin ng eval ang file. Hindi rin niya alam ang √, π, degrees, o na ang 2π ay 2×π. Calculator syntax lang ang tinatanggap ng parser, kaya safe siya, at kaya niyang magbigay ng friendly na error messages.
Paano niya nalalaman na × muna bago +?
Hagdan ng mga function ang parser. Hawak ng expr ang + at −, pero hinihingi niya sa term ang mga piraso sa magkabilang side. Hawak ng term ang × at ÷, at hinihingi rin niya sa susunod na baitang ang mga piraso niya. Unang natatapos ang mga mas mababang baitang, kaya nauuna ang × at ÷ bago ang + at −.
Ano'ng nangyayari kapag nag-divide sa zero?
Nagre-raise ang Python ng ZeroDivisionError. Sinasalo 'yon ng engine gamit ang except at nagre-raise siya ng sarili niyang CalculatorError("Cannot divide by zero"). Sinasalo naman 'yon ng window at ipinapakita nang pula ang message, at umuuga nang kaunti ang display. Walang nagka-crash.
Sabi ng Python, ang 0.1 + 0.2 ay 0.30000000000000004. Bakit 0.3 ang nasa calculator?
Naka-binary ang pag-store ng numbers sa computer, at hindi maiistore nang eksakto ang 0.1, gaya ng hindi maisusulat nang eksakto ang 1/3 bilang decimal. Nasa ika-17 digit ang maliit na error. 12 significant digits lang ang ipinapakita ng format_number, kaya natatago ang error.
Bakit eksaktong 0 ang sin(180) at hindi 0.000000000000000122?
Sa loob ng computer, hindi eksakto ang π, kaya 1.2 × 10⁻¹⁶ ang lumalabas sa sin(π). Alam ng helper na _snap kung gaano kalaki ang pwedeng rounding error para sa isang angle, at ginagawa niyang malinis na 0 ang kahit anong mas maliit doon. Hindi ginagalaw ang totoong maliliit na sagot, tulad ng sin(1E−13) sa radians.
Paano ginawa ang rounded na keys?
Hindi kayang gumuhit ng Tkinter ng rounded na button, kaya gumagawa ang app ng larawan para sa bawat kulay at laki ng key. Makinis ang corners kasi halo ng dalawang kulay ang mga pixel sa gilid. Nakaguhit ang 53 keys sa iisang canvas, kaya mabilis pa rin ang pag-resize.
Paano mo nalalaman na gumagana siya?
Tatlong paraan: 43 unit tests na nagche-check ng eksaktong sagot, 120,000 random na input na hindi nakapag-crash sa kanya, at pagkumpara sa sariling math ng Python sa 38,000 random na expression, na lahat ay nagtugma.
Ano ang idadagdag mo next?
Ilang idea: light theme, pag-save ng history sa isang file, dagdag na functions tulad ng nCr para sa combinations, o graph mode na gumuguhit ng y = sin(x).
- patakbuhin ang calculator at ang tests niya sa terminal
- gamitin ang memory, Ans, EXP at ang DEG/RAD switch
- sabihin kung ano ang token, may kasamang halimbawa
- ipaliwanag kung bakit nauuna ang × bago ang +, gamit ang hagdan
- ituro kung saan nagiging friendly na message ang mga error
- ipaliwanag kung bakit hindi kailangang buksan ng tests ang window
Cheat sheet
| Gusto mong… | Code | Resulta |
|---|---|---|
| Magpakita sa screen | print("Hi", 5) | Hi 5 |
| Magtanong sa user | x = input("? ") | laging str |
| Mag-convert ng type | int("7"), float("1.5"), str(7) | 7, 1.5, "7" |
| Alamin ang type | type(3.0) | <class 'float'> |
| Bilangin ang haba | len("hello") | 5 |
| Kunin ang parte ng text | "Python"[0:2] | "Py" |
| Magdagdag sa list | items.append(x) | nasa dulo |
| Kumuha sa dict | student["name"] | value ng key |
| Integer division / remainder | 7 // 2, 7 % 2 | 3, 1 |
| Exponent | 2 ** 3 | 8 |
| Ulitin nang N beses | for i in range(N): | i = 0 … N-1 |