Python Syntax Survival: Membaca, Menulis, dan Menjalankan Program
Part 004 — Python Syntax Survival: Membaca, Menulis, dan Menjalankan Program
Panduan syntax survival Python untuk software engineer: struktur file, indentation, expression, statement, values, names, conditionals, loops, functions, imports, I/O, dan membaca traceback.
Part 004 — Python Syntax Survival: Membaca, Menulis, dan Menjalankan Program
1. Tujuan Part Ini
Part ini adalah pintu masuk coding.
Kita akan mempelajari Python secukupnya untuk mulai menulis program kecil, menjalankannya, membaca error, dan memperbaiki kode sendiri.
Target part ini bukan menghafal seluruh syntax Python.
Targetnya:
- Membaca file Python sederhana.
- Menulis statement dan expression dasar.
- Memahami indentation.
- Memakai value dasar.
- Memakai name binding.
- Membuat conditional.
- Membuat loop.
- Membuat function.
- Melakukan import dasar.
- Membaca input/output sederhana.
- Membaca traceback.
- Membuat program kecil yang bisa dijalankan.
Kita tetap menjaga gaya belajar Kaufman:
cukup teori -> praktik -> error -> self-correction -> refactor
2. Struktur File Python
File Python biasanya berekstensi .py.
Contoh:
hello.py
Isi:
print("Hello, Python")
Jalankan:
python hello.py
Python mengeksekusi file dari atas ke bawah.
Contoh:
print("step 1")
print("step 2")
print("step 3")
Output:
step 1
step 2
step 3
Ini terlihat sederhana, tetapi penting: top-level code akan dijalankan saat file dieksekusi. Jika file di-import sebagai module, top-level code juga dieksekusi pada import pertama. Karena itu, production-style Python biasanya menghindari side effect berat di top-level.
3. Statement dan Expression
Python punya statement dan expression.
3.1 Expression
Expression menghasilkan value.
Contoh:
1 + 2
"CASE-" + "001"
len("Python")
status == "DRAFT"
Expression bisa dipakai di tempat yang membutuhkan value:
case_id = "CASE-" + "001"
is_draft = status == "DRAFT"
3.2 Statement
Statement melakukan aksi atau membentuk struktur program.
Contoh:
case_id = "CASE-001"
if case_id:
print(case_id)
def describe_case():
return "case"
Assignment, if, for, def, class, import, dan return adalah statement.
Mental model:
| Konsep | Pertanyaan |
|---|---|
| Expression | “Ini menghasilkan value apa?” |
| Statement | “Ini melakukan apa atau membentuk struktur apa?” |
4. Indentation adalah Struktur
Python memakai indentation untuk block.
Contoh:
status = "DRAFT"
if status == "DRAFT":
print("Case is still draft")
print("Submit it before review")
Dua print berada dalam block if.
Jika indentation salah:
if status == "DRAFT":
print("invalid")
Akan muncul IndentationError.
4.1 Gunakan 4 Spasi
Konvensi umum Python adalah 4 spasi per indentation level.
Jangan campur tab dan spasi.
Contoh nesting:
status = "SUBMITTED"
assignee = "reviewer-1"
if status == "SUBMITTED":
if assignee:
print("Ready for review")
else:
print("Needs assignment")
Nesting terlalu dalam biasanya tanda desain perlu disederhanakan.
Refactor dengan guard clause:
def review_message(status: str, assignee: str | None) -> str:
if status != "SUBMITTED":
return "Not ready"
if not assignee:
return "Needs assignment"
return "Ready for review"
5. Comments
Comment dimulai dengan #.
# This is a comment
case_id = "CASE-001"
Comment baik menjelaskan why, bukan mengulang what.
Kurang baik:
# Set status to DRAFT
status = "DRAFT"
Lebih baik:
# New cases must start as draft until intake validation completes.
status = "DRAFT"
Tetapi jangan memakai comment untuk menutupi kode yang buruk. Jika nama dan struktur bisa diperjelas, perbaiki kode dulu.
6. Values Dasar
Python punya beberapa value dasar yang sering dipakai.
6.1 Integer
case_count = 10
next_count = case_count + 1
6.2 Float
risk_score = 0.85
Untuk uang atau perhitungan presisi tinggi, jangan sembarangan memakai float. Gunakan decimal.Decimal jika butuh decimal precision.
6.3 Boolean
is_open = True
is_closed = False
Boolean sering muncul dari comparison:
is_draft = status == "DRAFT"
6.4 String
case_id = "CASE-001"
title = "Late reporting"
String bisa dibuat dengan single quote atau double quote:
a = "hello"
b = 'hello'
Pilih konsisten. Formatter biasanya membantu.
6.5 None
None merepresentasikan absence of value.
assigned_to = None
Cek None dengan is, bukan ==.
if assigned_to is None:
print("Unassigned")
Kenapa?
Karena None adalah singleton object. is None menyatakan identity check yang tepat.
7. Name Binding
Assignment mengikat nama ke object.
status = "DRAFT"
Artinya nama status menunjuk ke object string "DRAFT".
Rebinding:
status = "SUBMITTED"
Nama status sekarang menunjuk ke object lain.
Contoh dengan list:
notes = []
notes.append("created")
Ini bukan rebinding. Ini mutation terhadap object list.
Bedakan:
notes = []
other_notes = notes
other_notes.append("created")
print(notes)
Output:
['created']
Kedua nama menunjuk object list yang sama.
8. Naming Convention
Python umumnya memakai snake_case.
case_id = "CASE-001"
review_status = "UNDER_REVIEW"
Function:
def describe_case():
...
Class:
class Case:
...
Constant:
DEFAULT_STATUS = "DRAFT"
Private-by-convention:
def _normalize_status(status: str) -> str:
return status.strip().upper()
Python tidak punya private modifier seperti Java/C#. Prefix _ adalah sinyal convention: “internal, jangan dipakai langsung dari luar module”.
9. Operators Dasar
9.1 Arithmetic
a = 10 + 2
b = 10 - 2
c = 10 * 2
d = 10 / 2
e = 10 // 3
f = 10 % 3
g = 2**3
Catatan:
/menghasilkan float.//floor division.%modulo.**exponent.
9.2 Comparison
status == "DRAFT"
status != "CLOSED"
count > 0
count >= 1
9.3 Boolean Operators
is_open = status != "CLOSED"
is_high_priority = priority == "HIGH"
if is_open and is_high_priority:
print("Needs attention")
Operator:
and;or;not.
9.4 Membership
status in {"DRAFT", "SUBMITTED", "UNDER_REVIEW"}
Membership check sangat umum di Python.
10. Truthiness
Python punya konsep truthiness. Banyak value bisa dipakai dalam kondisi.
Falsy values umum:
False;None;0;0.0;"";[];{};set();tuple().
Contoh:
notes = []
if notes:
print("Has notes")
else:
print("No notes")
Ini idiomatik.
Tetapi hati-hati jika perbedaan antara None dan empty collection penting.
def process(notes: list[str] | None) -> None:
if notes is None:
print("Notes not loaded")
elif not notes:
print("Notes loaded but empty")
else:
print("Has notes")
Jangan memakai truthiness jika domain membutuhkan perbedaan eksplisit.
11. String Formatting
Gunakan f-string untuk formatting umum.
case_id = "CASE-001"
status = "DRAFT"
message = f"{case_id} is {status}"
Expression bisa dipakai di dalam f-string:
count = 3
print(f"Total cases: {count + 1}")
Untuk debug cepat:
print(f"{case_id=}")
Output:
case_id='CASE-001'
Namun untuk production diagnostics, gunakan logging, bukan print.
12. Collections Survival
Detail collection akan dibahas di part 007. Di sini kita cukup survival.
12.1 List
Ordered, mutable sequence.
cases = ["CASE-001", "CASE-002"]
cases.append("CASE-003")
Iterasi:
for case_id in cases:
print(case_id)
12.2 Tuple
Ordered, biasanya dipakai untuk data yang tidak dimutasi.
point = (10, 20)
Untuk return multiple values:
def split_case_id(case_id: str) -> tuple[str, str]:
prefix, number = case_id.split("-")
return prefix, number
12.3 Dict
Key-value mapping.
case = {
"id": "CASE-001",
"status": "DRAFT",
}
Access:
print(case["id"])
Safe access dengan default:
status = case.get("status", "UNKNOWN")
Namun jangan gunakan .get() untuk menyembunyikan missing required field tanpa keputusan domain.
12.4 Set
Unordered collection of unique elements.
allowed_statuses = {"DRAFT", "SUBMITTED", "UNDER_REVIEW"}
Membership cepat dan ekspresif:
if status in allowed_statuses:
print("valid")
13. Conditional
Basic if:
status = "DRAFT"
if status == "DRAFT":
print("Not submitted")
if/else:
if status == "CLOSED":
print("Done")
else:
print("Still active")
if/elif/else:
if status == "DRAFT":
print("Draft")
elif status == "SUBMITTED":
print("Submitted")
elif status == "UNDER_REVIEW":
print("Under review")
else:
print("Other")
13.1 Guard Clause
Daripada nesting dalam, gunakan guard clause.
Kurang baik:
def assign_case(status: str, assignee: str | None) -> str:
if status == "SUBMITTED":
if assignee is not None:
return "Assigned"
else:
return "Missing assignee"
else:
return "Not assignable"
Lebih baik:
def assign_case(status: str, assignee: str | None) -> str:
if status != "SUBMITTED":
return "Not assignable"
if assignee is None:
return "Missing assignee"
return "Assigned"
Guard clause membuat failure condition terlihat lebih awal.
14. Loops
14.1 for Loop
case_ids = ["CASE-001", "CASE-002", "CASE-003"]
for case_id in case_ids:
print(case_id)
Dengan index:
for index, case_id in enumerate(case_ids, start=1):
print(index, case_id)
14.2 Loop Dict
case = {
"id": "CASE-001",
"status": "DRAFT",
}
for key, value in case.items():
print(key, value)
14.3 while Loop
attempts = 0
while attempts < 3:
print("Trying...")
attempts += 1
Gunakan while untuk kondisi yang tidak berbasis collection. Untuk iterasi collection, for biasanya lebih tepat.
14.4 break dan continue
for case_id in case_ids:
if case_id == "CASE-002":
break
print(case_id)
for case_id in case_ids:
if case_id == "CASE-002":
continue
print(case_id)
Gunakan secukupnya. Terlalu banyak break/continue bisa membuat flow sulit dibaca.
15. Functions
Function adalah unit desain paling penting di awal.
def describe_case(case_id: str, status: str) -> str:
return f"{case_id} is {status}"
Panggil:
message = describe_case("CASE-001", "DRAFT")
print(message)
15.1 Parameter dan Return
def is_closed(status: str) -> bool:
return status == "CLOSED"
15.2 Default Parameter
def create_case(case_id: str, status: str = "DRAFT") -> dict:
return {
"id": case_id,
"status": status,
}
Hati-hati dengan mutable default:
def create_case(case_id: str, notes: list[str] = []) -> dict:
...
Jangan lakukan ini.
Gunakan:
def create_case(case_id: str, notes: list[str] | None = None) -> dict:
if notes is None:
notes = []
return {
"id": case_id,
"notes": notes,
}
15.3 Function Harus Punya Intent Jelas
Kurang baik:
def process(x):
...
Lebih baik:
def validate_case_status(status: str) -> bool:
...
Function name adalah dokumentasi desain.
16. Type Hints Survival
Type hints tidak wajib untuk runtime, tetapi sangat berguna untuk engineer.
def transition_case(status: str, target_status: str) -> str:
return target_status
Variable annotation:
case_id: str = "CASE-001"
case_count: int = 10
Collection annotation:
case_ids: list[str] = ["CASE-001", "CASE-002"]
case_by_id: dict[str, dict] = {}
Optional value:
assigned_to: str | None = None
Untuk 20 jam pertama, gunakan type hints pada function signature. Ini membantu:
- membaca intent;
- IDE autocomplete;
- static checking nanti;
- refactor;
- komunikasi antar engineer.
Jangan terlalu cepat masuk typing advanced. Fokus dulu pada clarity.
17. Imports
Import memakai kode dari module lain.
import json
Gunakan:
data = json.dumps({"id": "CASE-001"})
Import function tertentu:
from pathlib import Path
Gunakan:
path = Path("cases.json")
17.1 Hindari Wildcard Import
Hindari:
from math import *
Lebih baik:
from math import sqrt
Atau:
import math
math.sqrt(9)
Wildcard import membuat asal nama tidak jelas.
17.2 Import Lokal vs Global
Biasanya import diletakkan di atas file.
from pathlib import Path
import json
Import di dalam function boleh jika:
- dependency berat;
- menghindari circular import sementara;
- optional dependency;
- mempercepat startup path tertentu.
Tetapi jangan gunakan local import untuk menyembunyikan dependency design yang buruk.
18. Input dan Output Sederhana
18.1 Output
print("Hello")
Untuk debug awal, print cukup. Untuk production, gunakan logging.
18.2 Input
name = input("Name: ")
print(f"Hello, {name}")
input() selalu mengembalikan string.
Jika butuh angka:
raw_count = input("Count: ")
count = int(raw_count)
Ini bisa gagal jika input bukan angka. Nanti kita perlu error handling.
18.3 File I/O Sederhana
from pathlib import Path
path = Path("cases.txt")
path.write_text("CASE-001", encoding="utf-8")
content = path.read_text(encoding="utf-8")
print(content)
Untuk JSON:
import json
from pathlib import Path
case = {"id": "CASE-001", "status": "DRAFT"}
path = Path("case.json")
path.write_text(json.dumps(case, indent=2), encoding="utf-8")
loaded_case = json.loads(path.read_text(encoding="utf-8"))
print(loaded_case)
19. Exceptions Survival
Exception adalah mekanisme error utama di Python.
Contoh error:
case = {"id": "CASE-001"}
print(case["status"])
Akan menghasilkan KeyError.
Tangani jika memang bisa dipulihkan:
try:
print(case["status"])
except KeyError:
print("Missing status")
Namun jangan menangkap semua error tanpa alasan:
try:
...
except Exception:
pass
Ini buruk karena menyembunyikan bug.
19.1 Raise Exception
def validate_status(status: str) -> None:
allowed = {"DRAFT", "SUBMITTED", "UNDER_REVIEW", "CLOSED"}
if status not in allowed:
raise ValueError(f"Invalid status: {status}")
Panggil:
validate_status("INVALID")
19.2 Custom Exception Nanti
Untuk awal, ValueError cukup. Nanti kita akan membuat custom exception seperti:
class InvalidCaseTransitionError(Exception):
pass
20. Membaca Traceback
Traceback adalah peta menuju error.
Contoh file:
def get_status(case: dict) -> str:
return case["status"]
case = {"id": "CASE-001"}
print(get_status(case))
Error:
Traceback (most recent call last):
File "example.py", line 5, in <module>
print(get_status(case))
File "example.py", line 2, in get_status
return case["status"]
KeyError: 'status'
Cara baca:
- Lihat baris terakhir: jenis error dan pesan.
- Lihat frame paling bawah sebelum error: lokasi yang memicu error.
- Naik ke atas untuk melihat call chain.
- Bedakan root cause dan caller.
Dalam contoh:
- Error type:
KeyError. - Missing key:
'status'. - Lokasi:
return case["status"]. - Caller:
print(get_status(case)).
Perbaikan bukan selalu .get(). Bisa jadi validasi data harus dilakukan lebih awal.
21. main() Pattern
Untuk program kecil, gunakan main().
def main() -> int:
print("Hello")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Manfaat:
- kode bisa di-import tanpa langsung menjalankan program;
- entry point jelas;
- mudah ditest;
- exit code eksplisit;
- cocok untuk CLI.
Test:
from hello import main
def test_main_returns_success():
assert main() == 0
22. Mini Program: Case Status Reporter
Buat file:
scratch/case_status_reporter.py
Isi:
ALLOWED_STATUSES = {"DRAFT", "SUBMITTED", "UNDER_REVIEW", "ESCALATED", "CLOSED"}
def normalize_status(status: str) -> str:
return status.strip().upper()
def validate_status(status: str) -> None:
if status not in ALLOWED_STATUSES:
raise ValueError(f"Invalid status: {status}")
def describe_case(case_id: str, status: str) -> str:
normalized_status = normalize_status(status)
validate_status(normalized_status)
return f"{case_id} is currently {normalized_status}"
def main() -> int:
case_id = input("Case ID: ")
status = input("Status: ")
try:
message = describe_case(case_id, status)
except ValueError as error:
print(f"Error: {error}")
return 1
print(message)
return 0
if __name__ == "__main__":
raise SystemExit(main())
Jalankan:
python scratch/case_status_reporter.py
Coba input:
Case ID: CASE-001
Status: draft
Output:
CASE-001 is currently DRAFT
Coba input invalid:
Case ID: CASE-001
Status: waiting
Output:
Error: Invalid status: WAITING
Konsep yang dipakai:
- constant;
- set;
- function;
- string normalization;
- validation;
- exception;
- input;
- output;
- exit code;
main()pattern.
23. Mini Program dengan Data Structure
Versi berikut memakai dictionary.
ALLOWED_STATUSES = {"DRAFT", "SUBMITTED", "UNDER_REVIEW", "ESCALATED", "CLOSED"}
def normalize_status(status: str) -> str:
return status.strip().upper()
def validate_status(status: str) -> None:
if status not in ALLOWED_STATUSES:
raise ValueError(f"Invalid status: {status}")
def create_case(case_id: str, title: str, status: str = "DRAFT") -> dict:
normalized_status = normalize_status(status)
validate_status(normalized_status)
return {
"id": case_id,
"title": title,
"status": normalized_status,
"notes": [],
}
def add_note(case: dict, note: str) -> dict:
updated_case = dict(case)
updated_case["notes"] = list(case["notes"])
updated_case["notes"].append(note)
return updated_case
def describe_case(case: dict) -> str:
return f"{case['id']} [{case['status']}] {case['title']}"
def main() -> int:
case = create_case("CASE-001", "Late reporting")
case = add_note(case, "Created from syntax survival program")
print(describe_case(case))
print(case["notes"])
return 0
if __name__ == "__main__":
raise SystemExit(main())
Perhatikan add_note().
Kita tidak langsung mutate object input:
case["notes"].append(note)
Kita membuat copy:
updated_case = dict(case)
updated_case["notes"] = list(case["notes"])
Ini bukan selalu wajib, tetapi latihan bagus untuk menyadari mutability.
24. Debug Drill
Ambil kode ini:
def create_case(case_id: str, title: str, status: str = "DRAFT") -> dict:
return {
"id": case_id,
"title": title,
"status": status,
}
case = create_case("CASE-001", "Late reporting")
print(case["priority"])
Error apa yang terjadi?
Jawaban:
KeyError: 'priority'
Perbaikan opsi:
Opsi 1 — Tambahkan field default
def create_case(
case_id: str,
title: str,
status: str = "DRAFT",
priority: str = "MEDIUM",
) -> dict:
return {
"id": case_id,
"title": title,
"status": status,
"priority": priority,
}
Opsi 2 — Gunakan .get()
print(case.get("priority", "MEDIUM"))
Opsi 3 — Validasi schema
def require_field(data: dict, field: str) -> None:
if field not in data:
raise ValueError(f"Missing required field: {field}")
Mana yang benar?
Tergantung domain.
Jika priority wajib, .get() mungkin menyembunyikan data rusak. Jika priority optional, default mungkin tepat.
25. Syntax Anti-Patterns Awal
25.1 One Giant Script
Buruk:
# 300 lines of mixed input, validation, storage, printing
Lebih baik:
- pisahkan function;
- pisahkan domain logic;
- gunakan
main().
25.2 Bare Except
Buruk:
try:
do_work()
except:
pass
Lebih baik:
try:
do_work()
except ValueError as error:
print(f"Invalid input: {error}")
25.3 Overusing Abbreviations
Buruk:
def proc(c):
return c["st"] == "D"
Lebih baik:
def is_draft(case: dict) -> bool:
return case["status"] == "DRAFT"
25.4 Clever One-Liners
Buruk untuk domain rule penting:
x = [c for c in cases if c["s"] in a and not c["d"]]
Lebih baik:
def is_actionable(case: dict) -> bool:
return case["status"] in ACTIONABLE_STATUSES and not case["deleted"]
actionable_cases = [case for case in cases if is_actionable(case)]
25.5 Silent Type Conversion
Buruk:
count = input("Count: ")
total = count + 1
Error karena input() mengembalikan string.
Lebih baik:
raw_count = input("Count: ")
count = int(raw_count)
total = count + 1
Lalu tangani ValueError jika input invalid.
26. Practice Set
Latihan 1 — Basic Values
Buat file:
scratch/basic_values.py
Isi variable:
case_id;title;status;priority;assigned_to.
Print kalimat:
CASE-001 [DRAFT] Late reporting assigned to unassigned
Jika assigned_to is None, tampilkan "unassigned".
Latihan 2 — Status Validation
Buat function:
def is_valid_status(status: str) -> bool:
...
Status valid:
DRAFT;SUBMITTED;UNDER_REVIEW;ESCALATED;CLOSED.
Test manual dengan beberapa input.
Latihan 3 — Normalize Status
Buat:
def normalize_status(status: str) -> str:
...
Input:
" draft "
Output:
"DRAFT"
Latihan 4 — Describe Case
Buat:
def describe_case(case: dict) -> str:
...
Input:
{
"id": "CASE-001",
"title": "Late reporting",
"status": "DRAFT",
}
Output:
CASE-001 [DRAFT] Late reporting
Latihan 5 — Add Note Without Mutating Original
Buat:
def add_note(case: dict, note: str) -> dict:
...
Pastikan original case tidak berubah.
Hint:
updated_case = dict(case)
updated_case["notes"] = list(case["notes"])
Latihan 6 — Read Traceback
Buat error sengaja:
def get_priority(case: dict) -> str:
return case["priority"]
case = {"id": "CASE-001"}
print(get_priority(case))
Tulis jawaban:
- Error type?
- Missing key?
- Line yang error?
- Caller-nya siapa?
- Perbaikan yang paling sesuai?
27. Self-Check
Jawab tanpa melihat materi:
- Apa beda statement dan expression?
- Kenapa indentation penting di Python?
- Apa beda
is Nonedan== None? - Apa itu truthiness?
- Apa risiko memakai truthiness untuk domain data?
- Apa beda mutation dan rebinding?
- Kenapa mutable default argument berbahaya?
- Kapan memakai
for, kapan memakaiwhile? - Kenapa wildcard import buruk?
- Apa isi minimal
main()pattern? - Bagaimana membaca traceback?
- Kenapa
.get()tidak selalu solusi terbaik untuk missing key? - Kenapa function name penting?
- Apa manfaat type hints walau runtime tidak enforce?
- Kenapa kode top-level harus hati-hati?
28. Definition of Done Part 004
Kamu selesai part ini jika:
- Bisa menjalankan file Python.
- Bisa membuat variable dasar.
- Bisa memakai
if/elif/else. - Bisa memakai
forloop. - Bisa membuat function dengan return value.
- Bisa memakai
list,dict,setdasar. - Bisa memakai f-string.
- Bisa membaca input dengan
input(). - Bisa menulis dan membaca file sederhana.
- Bisa menangkap
ValueErrorsederhana. - Bisa membaca traceback
KeyError. - Bisa memakai
main()pattern. - Bisa menjelaskan mutation vs rebinding lewat contoh.
- Bisa membuat program
case_status_reporter.py. - Bisa menyebutkan minimal 5 anti-pattern syntax awal.
29. Ringkasan
Part ini memberi syntax survival.
Yang paling penting bukan jumlah syntax yang dihafal, tetapi kemampuan menjalankan loop:
write small code -> run -> observe -> read error -> fix -> simplify
Kamu sekarang punya fondasi untuk:
- menulis program Python kecil;
- memahami block;
- memakai value dasar;
- membuat function;
- melakukan validation;
- memakai collection awal;
- membaca traceback;
- mulai memisahkan logic dari I/O.
Part berikutnya akan menggunakan fondasi ini untuk membangun mini project pertama: case-tracker sebagai latihan 20 jam pertama yang mengikat syntax, environment, function, collection, error, dan test.
30. Referensi
- Python Documentation — The Python Tutorial: https://docs.python.org/3/tutorial/
- Python Documentation — Built-in Types: https://docs.python.org/3/library/stdtypes.html
- Python Documentation — Errors and Exceptions: https://docs.python.org/3/tutorial/errors.html
- Python Documentation — More Control Flow Tools: https://docs.python.org/3/tutorial/controlflow.html
- Python Documentation — Modules: https://docs.python.org/3/tutorial/modules.html
You just completed lesson 04 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.