Python starter notes · Taglish editionRead in English →

Python, gets mo na?

Mga notes para sa first-time coder. Bawat topic may maikling paliwanag, code na pwede mong basahin line by line, at visual na pwede mong pindutin para makita mo kung ano talaga ang nangyayari sa loob ng Python.

psst →

Huwag mong i-memorize. I-click mo ang Next sa mga lab at panoorin mo kung paano gumagalaw ang mga variable. Doon mo talaga mage-gets.

# unang program mo
pangalan = "Ashley"
print("Kaya mo 'to,", pangalan + "!")
Kaya mo 'to, Ashley!
int19 float1.75 str"BSCS" boolTrue list[1, 2, 3] dict{"yr": 1} functiongwa()

Color code ng mga data type. Pareho ang kulay nila sa buong page, para kahit sa isang tingin alam mo na kung anong klase ng value ang nakikita mo.

Lesson 01

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.

Lab · step throughpindutin ang Next ↓
Tandaan

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.

Lesson 02

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!).

Lab · panoorin ang mga tagyellow = kakabago lang
Rules sa pagpangalan
  • Letters, numbers, at _ lang. Bawal mag-umpisa sa number.
  • Case-sensitive: magkaiba ang Edad at edad.
  • Gumamit ng snake_case: final_grade, hindi finalgrade.
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.

Lesson 03

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().

TypePara saanHalimbawa
intintegerWhole numbers, walang decimal19, -5, 2026
floatdecimalNumbers na may decimal point1.75, 3.0, -0.5
strstringText, laging naka-quotes"Ana", 'BSIT', "19"
boolbooleanOo o hindi lang: True o FalseTrue, False
Lab · hulaan ang typei-tap bawat card

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)
TypeError: can only concatenate str (not "int") to str

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".

Lesson 04

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.

Lab · indexing at slicinggalawin ang sliders

Pwede ka ring mag-click ng isang kahon para makita ang salita[i] nito.

Ang slice rule

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!
Lesson 05

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.

Lab · i-edit ang barkada listsubukan mo i-pop hanggang maubos
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
Tandaan

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.

Lesson 06

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 { }.

Lab · i-lookup ang student recordpumili ng key
student = {"name": "Ana", "course": "BSCS", "year": 1, "scholar": True}
List o dict?

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).

Lesson 07

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.

Lab · galawin ang scoretingnan kung saan siya papasok

Comparison operators

==magkapareho ba?
!=magkaiba ba?
> <mas malaki / mas maliit
>= <=mas malaki o equal / mas maliit o equal
and or notpagsamahin ang mga kondisyon
Indentation = block

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.

Lesson 08

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.

Lab · bilangin ang baonbantayan si b at total

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.

Lab · range(start, stop, step)pula = stop (hindi kasama)

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
Lesson 09

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.

Lab · kompyutin ang GWAmay sariling "kwarto" ang function
return vs print

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
Lesson 10

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.

SyntaxError
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.

IndentationError
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).

NameError
pangalan = "Ana"
print(Pangalan)

Hindi kilala ng Python ang pangalan. Kadalasan typo o maling capitalization. pangalan ≠ Pangalan.

TypeError
edad = input("Edad? ")
print(edad + 1)

Pinaghalo ang magkaibang type. Ang input() ay str, kaya i-int() mo muna bago i-add.

IndexError
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].

ZeroDivisionError
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:

Lesson 11

Quick quiz

Anim na tanong lang. Pumili ng sagot para makita agad kung tama, at bakit.

0 / 6
Lesson 12 · Capstone

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

  1. 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.

  2. Ilagay ang dalawang file sa iisang folder.

    Halimbawa sa Documents\calculator, at nasa loob ang scientific_calculator.py (ang app) at test_scientific_calculator.py (ang tests niya).

  3. 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.

  4. Patakbuhin ang calculator.
    > python scientific_calculator.py

    Lalabas ang window. Isara mo lang siya gaya ng ibang window kapag tapos ka na.

  5. 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 OK sa 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 StoreHindi 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 directoryNasa 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 testsMay 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

Ang window ng calculator: madilim na display sa itaas na may 1÷7+cos(60 at live na sagot sa ilalim, scientific keys sa kaliwa, number pad sa kanan na may violet na equals key, at history panel sa pinakakanan. 1 2 3 4 5 6 7 8 9 10
  1. 1Status chips. Ang DEG o RAD ay ang angle mode. Lumalabas ang M 3.14159 habang may naka-save sa memory.
  2. 2Ipakita / itago ang history. Lumiliit ang window kapag nakatago.
  3. 3Huling na-compute, tulad ng 2π×sin(30) =. Dito rin lumalabas nang pula ang mga error.
  4. 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.
  5. 5Live preview. Lumalabas na ang sagot habang nagta-type ka, bago mo pa pindutin ang =.
  6. 6Scientific keys: trig, powers, roots, logs at constants.
  7. 7Memory keys para mag-save ng number para mamaya.
  8. 8Number pad kasama ang apat na operator, brackets, clear at backspace.
  9. 9Equals: kino-compute ang sagot.
  10. 10History. Naka-save dito ang bawat sagot, pinakabago sa itaas. I-click ang isa para gamitin ulit.

Ano ang ginagawa ng bawat key

KeysGinagawa nilaSubukan
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.
sincostanMga trig function. Ang kabaligtaran nila ay sin⁻¹ cos⁻¹ tan⁻¹.sin(30) → 0.5
DEGNagpapalit sa degrees (DEG) at radians (RAD). Ipinapakita ng chip ① kung alin ang naka-on.
sinhcoshtanhMga 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ˣloglnPowers ng 10 at ng e, at ang kabaligtaran nila: base 10 ang log, base e ang ln.log(1000) → 3
1/xIsa 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
modAng natira (remainder) pagkatapos mag-divide.7 mod 3 → 1
|x|Absolute value: tinatanggal ang minus sign.abs(−5) → 5
πeAng constants na π = 3.14159… at e = 2.71828…2π → 6.28318530718
EXPScientific notation. Nagta-type ng malaking E: ang 6.02E23 ay 6.02 × 10²³.1.5E3 → 1500
AnsAng 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

PindutinMangyayari
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 / BackspaceBinubura lahat / binubura ang isang character.
↑ ↓Ibinabalik ang mga naunang na-compute, gaya sa terminal.
Ctrl+VNagpe-paste ng expression.
Tandaan
  • 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) at 2(3+4).
  • Ang maliit na e ay ang number na e. Ang malaking E (EXP) ay scientific notation.
  • Hanggang 12 digits ang ipinapakita ng display, kaya 0.3 ang lalabas sa 0.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)
  1. Ang pinakamahabang side ng right triangle na may sides na 3 at 4: √(3²+4²) → 5
  2. ₱1,000 na naka-ipon nang may 5% kada taon sa loob ng 10 taon: 1000×1.05^10 → 1628.89462678
  3. I-check ang sikat na rule: sin(37)²+cos(37)² → 1 (subukan kahit anong angle)
  4. Area ng bilog na radius 7: π×7² → 153.938040026
  5. Ilang paraan para pagsunud-sunurin ang 5 libro: 5! → 120
  6. Ilang minuto sa isang taon: 365×24, pindutin ang =, tapos ×60 (makikita mo sa display ang Ans×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.

→text

Ang engine

Kino-compute ng ScientificEngine.evaluate() ang sagot sa dalawang step:

  1. Hinahati ng _tokenize ang text sa maliliit na piraso (tokens).
  2. Binabasa ng _Parser ang mga piraso sa tamang pagkakasunod at nagko-compute.
→number
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

1–20Description, mga import, at CalculatorError, ang sariling error type ng app
22–75Symbol table (× → *), mga kilalang pangalan tulad ng sin at pi, at _tokenize()
77–188_Parser: binabasa ang tokens sa tamang pagkakasunod at kino-compute ang sagot
190–246Math helpers: powers, mod, factorial, at paglilinis ng maliliit na rounding error
248–316ScientificEngine: degrees o radians, Ans, friendly na error messages
318–395format_number(), complete_parentheses(), toggle_sign(): maliliit na text helper
397–519Drawing helpers: paghahalo ng kulay at paggawa ng makinis na rounded corners
521–598Ang itsura: THEME colors, KEY_STYLES, laki ng font ng display, ang app icon
599–822Custom widgets: RoundedButton, Card, Key at Keypad
823–1315CalculatorApp: ang window, keyboard, memory, live preview at history
1316–1327main(): sinisimulan ang app

Sundan ang isang calculation

Lab · ang calculator, step by steppindot ng keys, tapos =
DEG
Subukan ang mga 'to

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.

expr+ −huling ginagawa, kaya pinakamaluwag ang kapit
term× ÷ mod 2πkasama ang nakatagong × sa 2π at 3sin(30)
unary−x +xsign sa harap ng isang bagay
power^kanan pakaliwa: 2^3^2 ay 2^9 = 512, at ang −2^2 ay −4
postfix! % ² ³ ⁻¹para sa bagay na nasa mismong harap niya: ang 3²5 ay (3²)×5 = 45
primary5 π Ans sin(…) ( … )pinakamaliliit na piraso; ang bracket ay nagsisimula ulit sa expr
def expr(self):
    value = self.term()
    while True:
        if self._accept("+"):
            value += self.term()
        elif self._accept("-"):
            value -= self.term()
        else:
            return value
Kaya mo 'tong basahin!

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.

7digit÷operatorsinfunctionMRcontrolACclear=equals

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:

Ang aral dito

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
TestInputInaasahanLumabas

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:

Ang 3²5 ay naging 847288609443

Ginawang text na ^2 ang ², kaya naging 3^25 ang 3²5. Ayos: naging sariling tokens ang ² ³ ⁻¹ sa postfix na baitang.

2, tapos MR, ay naging 25

Dumikit sa 2 ang number galing sa memory. Ayos: kapag nasa likod mismo ng digit ang cursor, maglagay ng × sa gitna.

Ang 0.3 mod 0.1 ay naging 0.1

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.

Ang tan(45)! ay nagsabing "needs a whole number"

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.

Paano pag-aralan ang file na 'to
  • Patakbuhin at laruin muna.
  • Basahin ang maiikling helper: format_number at complete_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

  1. 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.
  2. 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.
  3. Pindutin ang =, tapos i-type ang ×2.Kapag tinuloy ko pagkatapos ng equals, sa sagot siya nagpapatuloy, na tinatawag na Ans.
  4. I-type ang sin(30 at pindutin ang =.Siya na ang nagsasara ng bracket. Degrees ang ibig sabihin ng DEG; itong button ang lumilipat sa radians.
  5. I-type ang 1÷0 at pindutin ang =.Message ang lumalabas kapag may error, hindi nagka-crash. Sa code, 'yan ang try at except.
  6. I-click ang isang sagot sa history panel.Naka-save sa isang list ang bawat sagot, at kapag kinlick ko, magagamit ko ulit.
  7. Buksan ang code sa _tokenize, tapos sa expr.Hinahati muna ang text sa tokens. Tapos ang parser ay parang hagdan ng mga function, isa sa bawat level ng order of operations.
  8. 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).

Ready ka na kapag kaya mong…
  • 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
Para sa review

Cheat sheet

Gusto mong…CodeResulta
Magpakita sa screenprint("Hi", 5)Hi 5
Magtanong sa userx = input("? ")laging str
Mag-convert ng typeint("7"), float("1.5"), str(7)7, 1.5, "7"
Alamin ang typetype(3.0)<class 'float'>
Bilangin ang habalen("hello")5
Kunin ang parte ng text"Python"[0:2]"Py"
Magdagdag sa listitems.append(x)nasa dulo
Kumuha sa dictstudent["name"]value ng key
Integer division / remainder7 // 2, 7 % 23, 1
Exponent2 ** 38
Ulitin nang N besesfor i in range(N):i = 0 … N-1