Python Syntax, Variables and Control FlowIntroductionPython programs are built by combining expressions, statements, data values, and control structures. For data science, these foundations are especially important because data-processing programs must be readable, reproducible, and resistant to invalid input. A program that produces the correct result for one dataset but fails when a value is missing, malformed, or outside an expected range is not reliable. This chapter develops the syntax and execution concepts required for practical Python programming. It covers names and variables, built-in data types, operators, expressions, input and output, conditional logic, iteration, exceptions, and Pythonic conventions. The emphasis is not merely on memorising syntax, but on understanding how Python evaluates instructions and how programmers design clear, testable solutions. Learning ObjectivesBy the end of this chapter, you should be able to: - explain Python’s indentation, statement, and naming rules;
- select suitable built-in data types for common data-science tasks;
- use operators and expressions correctly, including comparisons and Boolean logic;
- obtain input and produce formatted output safely;
- implement decisions with
if, elif, and else; - implement repetition with
for and while loops; - control loops using
break, continue, and pass; - distinguish syntax, runtime, and logical errors;
- handle expected failures with appropriate exceptions;
- write readable and idiomatic Python programs.
1. Python’s Execution Model and SyntaxPython is an interpreted, dynamically typed, general-purpose language. In normal execution, Python reads source code, parses it, compiles it to bytecode, and executes that bytecode using the Python interpreter. The implementation most commonly used is CPython, although other implementations exist. A Python program consists of statements. An assignment statement binds a name to an object: temperature = 22.5
city = "Nairobi" The name temperature does not contain a permanently fixed type. It refers to a float object at this point and could later be rebound to another object. The object itself has a type, and operations are determined by that type. value = 10
value = "ten" This is legal, although repeatedly changing the meaning of a name can reduce readability. In professional code, a name should normally retain a consistent conceptual meaning. Indentation and blocksPython uses indentation to define blocks rather than braces. A block follows a statement ending in a colon, such as an if, loop, function, or class definition. score = 74
if score >= 50:
result = "pass"
print(result)
else:
result = "fail"
print(result)All statements in the same block must have consistent indentation. Four spaces are the conventional choice. Mixing tabs and spaces can cause IndentationError or make code difficult to maintain. Comments and documentationA comment begins with # and is ignored by the interpreter. # Convert metres to kilometres
kilometres = metres / 1000 Comments should explain purpose, assumptions, or non-obvious decisions. They should not merely restate obvious syntax. Documentation strings, or docstrings, describe modules, functions, and classes. def celsius_to_fahrenheit(celsius):
"""Return a Celsius temperature converted to Fahrenheit."""
return celsius * 9 / 5 + 32Naming conventionsNames may contain letters, digits, and underscores, but cannot begin with a digit. Python is case-sensitive: total, Total, and TOTAL are different names. Use snake_case for variables and functions, PascalCase for classes, and uppercase names for constants by convention. Avoid shadowing built-in names: # Poor practice
list = [1, 2, 3]
# Better
values = [1, 2, 3] Shadowing list makes the built-in constructor unavailable through that name in the current scope. 2. Variables, Objects, and Data TypesA variable is best understood as a name bound to an object. Assignment does not copy a value automatically; it creates or changes a reference to an object. first = [10, 20]
second = first
second.append(30)
print(first) # [10, 20, 30] Both names refer to the same list. This is important when working with mutable objects. Core built-in types| Type | Example | Typical use |
|---|
int | 42 | Counts and whole-number quantities | float | 3.14 | Measurements and continuous numerical values | complex | 2 + 3j | Specialised numerical computation | bool | True | Logical conditions | str | "sales" | Text and categorical labels | list | [2, 4, 6] | Ordered, mutable collection | tuple | (2024, 12, 31) | Ordered, usually immutable collection | set | {"A", "B"} | Unique, unordered values | dict | {"id": 17} | Key–value mapping | NoneType | None | Absence of a value |
The type() function reports an object’s type, while isinstance() tests whether an object belongs to a type or compatible class. amount = 125
print(type(amount))
print(isinstance(amount, int)) Numeric types and conversionPython supports ordinary arithmetic on integers and floating-point numbers. Floating-point values are approximations, so direct equality comparisons may be unsafe for some calculations. print(0.1 + 0.2 == 0.3) # Often False For tolerance-based comparison, use math.isclose(): import math
math.isclose(0.1 + 0.2, 0.3) Conversions should be explicit and validated where input is external: count = int("25")
ratio = float("0.75")
label = str(25)The conversion int(3.9) truncates toward zero and returns 3; it does not round to the nearest integer. StringsStrings are ordered sequences of Unicode characters and are immutable. Indexing starts at zero. word = "Python"
print(word[0]) # P
print(word[-1]) # n
print(word[1:4]) # yth Useful string methods include .strip(), .lower(), .upper(), .split(), .replace(), and .startswith(). Methods return a new string rather than modifying the original. Formatted string literals, or f-strings, are usually the clearest way to construct output: name = "Amina"
score = 87.456
print(f"{name} achieved {score:.1f}%")The format specification .1f displays one digit after the decimal point. Collections and mutabilityLists are ordered and mutable. Tuples are ordered and immutable. Sets contain unique elements and are useful for membership tests. Dictionaries associate keys with values. scores = [68, 74, 81]
student = {"name": "Amina", "scores": scores}
unique_scores = set(scores)Dictionary access can raise KeyError if a key is absent. The .get() method provides a default: email = student.get("email", "not supplied")Truth-value testing applies to many objects. Empty strings, empty collections, numeric zero, and None are false-like; most other objects are true-like. if scores:
print("Scores are available")Use is None rather than == None when testing for the singleton None value. 3. Operators and ExpressionsAn expression produces a value. Operators combine values or transform them. Arithmetic operators| Operator | Meaning | Example |
|---|
+ | Addition or concatenation | 4 + 3 → 7 | - | Subtraction | 4 - 3 → 1 | * | Multiplication or repetition | 4 * 3 → 12 | / | True division | 7 / 2 → 3.5 | // | Floor division | 7 // 2 → 3 | % | Remainder | 7 % 2 → 1 | ** | Exponentiation | 2 ** 3 → 8 |
Floor division returns the mathematical floor of the quotient, not merely a truncated result. For negative values, -7 // 2 is -4 because −4 is the greatest integer less than or equal to −3.5. Operator precedence generally evaluates parentheses first, then exponentiation, multiplication/division/remainder, and addition/subtraction. Use parentheses when they improve clarity. Comparison and Boolean operatorsComparison operators return Boolean values: ==, !=, <, <=, >, and >=. Boolean operators combine conditions: and is true only when both operands are true;or is true when at least one operand is true;not reverses truth value.
Python uses short-circuit evaluation. In A and B, Python does not evaluate B if A is false. In A or B, it does not evaluate B if A is true. This can prevent errors: if record is not None and record["status"] == "valid":
process(record)The second condition is evaluated only when record is not None. Do not confuse assignment (=) with equality comparison (==). The identity operator is tests whether two names refer to the same object; it is not a general replacement for ==. Membership and assignment expressionsThe operators in and not in test membership: if "error" in message.lower():
flag_record()The assignment expression operator :=, introduced in Python 3.8, can assign within an expression. It should be used sparingly because clarity is more important than compactness. 4. Input and OutputThe input() function reads a line of text and always returns a string. Conversion is therefore required for numeric input. raw_age = input("Enter age: ")
age = int(raw_age)This code fails if the user enters non-numeric text. A reliable program validates or handles the conversion. try:
age = int(input("Enter age: "))
except ValueError:
print("Age must be a whole number.")Output is commonly produced with print(). Multiple arguments are separated by a space by default, and sep and end can change formatting. print("A", "B", sep=" | ")
print("Loading", end="...")For data-science workflows, avoid relying on visually formatted output as a data-storage method. Use structured formats such as CSV, JSON, or database tables when results must be reused. 5. Conditional LogicConditional statements select actions based on Boolean expressions. if temperature < 0:
category = "freezing"
elif temperature < 20:
category = "cool"
else:
category = "warm"Conditions are evaluated from top to bottom, and only the first matching branch executes. Therefore, branch order matters. A broad condition placed first can make later branches unreachable. Nested conditions are sometimes appropriate, but compound Boolean expressions are often clearer: if 0 <= score <= 100 and attendance >= 75:
eligible = True
else:
eligible = FalsePython’s chained comparison 0 <= score <= 100 is equivalent to (0 <= score) and (score <= 100). A conditional expression is suitable for a simple two-way choice: status = "pass" if score >= 50 else "fail" It should not be used for complex multi-step logic. Structural pattern matchingPython 3.10 introduced match and case, which can express choices based on a value or structure. command = "quit"
match command:
case "start":
print("Starting")
case "quit":
print("Stopping")
case _:
print("Unknown command")For simple value tests, if may remain more familiar. Pattern matching becomes valuable when processing structured records or multiple alternatives. 6. Iteration and RepetitionA for loop iterates over items in an iterable, such as a list, string, dictionary, set, file, or range. scores = [68, 74, 81]
for score in scores:
print(score)Use range() when a sequence of integer positions is required: for index in range(3):
print(index)range(3) produces 0, 1, and 2. The stop value is excluded. The general form is range(start, stop, step).
A while loop repeats while a condition remains true: attempts = 0
while attempts < 3:
print("Attempt", attempts + 1)
attempts += 1Every while loop must have a credible path toward termination. Failure to update the controlling state creates an infinite loop. Loop controlbreak exits the nearest loop. continue skips to the next iteration. pass performs no action and is useful as a temporary placeholder, not as a substitute for completed logic.
for value in values:
if value is None:
continue
if value < 0:
break
process(value)A loop may have an else clause. The else block executes if the loop finishes normally, but not if it exits through break. for number in numbers:
if number == target:
print("Found")
break
else:
print("Not found")Pythonic iterationPrefer direct iteration over manually managing indices when indices are unnecessary. Use enumerate() when both index and value are needed, and zip() to traverse related sequences. for position, score in enumerate(scores, start=1):
print(position, score)
for name, score in zip(names, scores):
print(name, score)Comprehensions provide concise construction of collections when the transformation is simple. squares = [number ** 2 for number in range(6)]
positive = [value for value in values if value > 0] Avoid excessively complex comprehensions; a conventional loop is better when several operations or conditions are involved. 7. Exceptions and Reliable ProgramsAn exception is an event indicating that normal execution cannot continue at a particular point. Common exceptions include ValueError, TypeError, IndexError, KeyError, FileNotFoundError, and ZeroDivisionError. The basic structure is: try:
value = int(user_text)
except ValueError:
print("Enter a valid integer.")
else:
print(f"Accepted: {value}")
finally:
print("Input attempt complete.")try contains code that may fail;except handles a specified exception;else runs when no exception occurs;finally runs whether or not an exception occurs.
Catch specific exceptions rather than using a bare except, which can hide programming errors and interrupt signals. A program may raise an exception deliberately when an input violates a contract: def percentage(value):
if not 0 <= value <= 100:
raise ValueError("percentage must be between 0 and 100")
return valueException handling should occur at the level that can make a meaningful decision. Do not silently discard errors. Record, report, recover, or terminate clearly according to the application’s requirements. 8. Worked Example: Validating and Summarising MeasurementsThe following program accepts measurements until the user enters done. It handles invalid input, rejects negative values, and calculates a mean only when valid data are available. measurements = []
while True:
text = input("Enter a non-negative measurement or 'done': ").strip()
if text.lower() == "done":
break
try:
measurement = float(text)
except ValueError:
print("Please enter a numeric value.")
continue
if measurement < 0:
print("Measurement cannot be negative.")
continue
measurements.append(measurement)
if measurements:
mean = sum(measurements) / len(measurements)
print(f"Count: {len(measurements)}")
print(f"Mean: {mean:.2f}")
else:
print("No valid measurements were entered.")The mean is calculated using: x̄ = (1/n) ∑ᵢ₌₁ⁿ xᵢ Here, xᵢ is the i-th valid measurement, n is the number of valid measurements, and x̄ is their arithmetic mean. The formula applies when n > 0; the program therefore checks that the list is non-empty before division. The measurements must also be comparable and expressed in compatible units. This example illustrates input conversion, validation, a sentinel value, exception handling, a while loop, continue, and guarded calculation. 9. Applied Case: Quality Screening of Sensor ReadingsBackgroundA manufacturing plant receives temperature readings from a sensor every minute. The data-ingestion script must classify readings as normal, warning, or invalid before they enter an analytics pipeline. The illustrative policy is that temperatures below 0 °C or above 120 °C are invalid, readings from 0 °C through 80 °C are normal, and readings above 80 °C through 120 °C require a warning. ProblemA naïve script might compare values without checking their type or might stop completely when one malformed reading appears. Such behaviour can lose an entire batch of otherwise usable observations. ApproachThe following function validates one raw reading and returns a structured result: def classify_reading(raw_value):
try:
value = float(raw_value)
except (TypeError, ValueError):
return {"status": "invalid", "reason": "not numeric"}
if value < 0 or value > 120:
return {"status": "invalid", "reason": "outside sensor range"}
if value > 80:
return {"status": "warning", "value": value}
return {"status": "normal", "value": value}
raw_readings = [22.4, "81.2", "offline", 121, 79.8]
results = [classify_reading(item) for item in raw_readings]OutcomeThe valid readings are classified without allowing the malformed string or out-of-range value to terminate the batch. A production implementation would also log invalid records, retain timestamps and sensor identifiers, and define what happens to missing values. Learning pointsReliable data-science code separates conversion, validation, classification, and reporting. It uses explicit thresholds, handles expected exceptions narrowly, and preserves enough context for later auditing. The policy itself must be documented because thresholds are domain assumptions, not universal Python rules. 10. Pythonic Conventions and Professional PracticeThe official style guide, PEP 8, recommends readable formatting, meaningful names, moderate line length, and consistent spacing. Important practices include: - Prefer clarity over cleverness.
- Use four spaces for indentation.
- Keep functions focused on one responsibility.
- Avoid duplicated logic.
- Use descriptive names such as
valid_measurements, not x when the meaning matters. - Validate data at system boundaries, such as user input or files.
- Use
with for resources that must be closed. - Write tests for normal, boundary, and invalid cases.
- Use version control and code review for collaborative work.
- Keep secrets, passwords, and personal data out of source code and logs.
Python’s conventions are not merely cosmetic. Consistent code reduces defects, makes peer review easier, and lowers the cost of modifying analytical pipelines. 11. Common Errors and LimitationsSyntax errors occur before a program can execute, for example a missing colon or unmatched parenthesis. Runtime errors occur during execution, such as dividing by zero. Logical errors allow execution but produce incorrect results, such as using the wrong threshold or calculating a mean over invalid records. Other recurring problems include: - using
= where == is required; - assuming
input() returns a number; - modifying a list while iterating over it;
- relying on exact equality for floating-point calculations;
- writing a loop with no terminating condition;
- catching every exception and hiding the underlying defect;
- confusing a shallow reference with an independent copy;
- accepting unvalidated external data;
- using a variable before it has been assigned.
Python’s dynamic typing supports rapid development but shifts some error detection from writing time to execution time. Type hints, linters, formatters, tests, and static analysis can reduce this risk, although they do not replace sound reasoning. 12. Contemporary Relevance to Data ScienceModern data-science programs frequently combine core Python control flow with libraries such as NumPy, pandas, and scikit-learn. Library operations may process entire arrays or columns at once, a practice called vectorisation. Nevertheless, control flow remains essential for validation, configuration, exception handling, model evaluation, and workflow orchestration. In artificial-intelligence systems, Python code also controls data-quality checks, experiment loops, evaluation criteria, and safety constraints. Automated tools can generate code, but generated code must be reviewed for security, correctness, privacy, computational cost, and hidden assumptions. Input validation is particularly important when code processes user-supplied files or runs in cloud environments. Chapter SummaryPython programs consist of statements and expressions whose meaning depends on objects, types, operators, and control flow. Names are bound to objects, and mutable objects can be shared by multiple names. Built-in types such as integers, floating-point numbers, strings, lists, sets, tuples, dictionaries, Booleans, and None support common data-science tasks. Expressions use arithmetic, comparison, Boolean, membership, and identity operators. Input arrives as text and should be converted and validated explicitly. Conditional statements select among alternatives, while for and while loops support repetition. break, continue, loop else, comprehensions, enumerate(), and zip() provide useful iteration patterns. Exceptions represent abnormal conditions and should be handled specifically at an appropriate level. Reliable programs distinguish invalid external data from programming defects, document assumptions, and test boundary cases. Pythonic code values readability, meaningful names, small responsibilities, explicit validation, and maintainable structure. Key TermsVariable: A name bound to an object. Object: A runtime entity with a value, identity, and type. Statement: An instruction executed by Python. Expression: Code that produces a value. Dynamic typing: A model in which names do not have permanently fixed types. Mutable: Capable of being changed after creation. Immutable: Not changeable after creation. Boolean: A truth value represented by True or False. Operator: A symbol or keyword that performs an operation. Short-circuit evaluation: Stopping Boolean evaluation when the result is already known. Iterable: An object whose elements can be visited sequentially. Indentation: Leading whitespace used to define Python blocks. Comprehension: Compact syntax for constructing a collection. Exception: An event signalling an abnormal execution condition. Validation: Checking whether data meet defined requirements. Sentinel value: A special value indicating termination or a particular state. PEP 8: Python’s widely used style guide. Type conversion: Transforming a value from one data type to another. Scope: The region in which a name can be accessed. Vectorisation: Applying an operation to a collection or array without an explicit Python-level loop. Review QuestionsShort Answer Questions- Why does Python use indentation, and what problem can inconsistent indentation cause?
- What is the difference between
== and is? - Why must the result of
input() usually be converted before numerical processing? - What is the purpose of the
else clause on a loop? - Why is catching a bare
except generally discouraged?
Long Answer Questions- Explain Python’s object-and-name model, including the consequences of assigning one mutable list to two names.
- Compare
for and while loops and give circumstances in which each is appropriate. - Explain arithmetic, comparison, Boolean, membership, and identity operators with examples.
- Describe the structure and purpose of
try, except, else, and finally. - Discuss how Pythonic conventions improve reliability and maintainability in data-science programs.
Analytical / Application Questions- Design a program that reads a sequence of examination marks, rejects values outside 0–100, and reports the mean and number of valid marks. Identify the exceptions and boundary cases that must be handled.
- A loop processes sensor records but occasionally receives
None, text, or negative values. Propose a validation and exception-handling strategy that preserves valid records and provides an audit trail. - A program classifies customer transactions using several thresholds, but the broadest condition is placed first and prevents later conditions from running. Explain the defect and redesign the conditional logic.
Multiple Choice Questions with AnswersQuestion 1What does the expression 7 // 2 evaluate to? A. 3 B. 3.5 C. 4 D. 1 Correct Answer: A. 3 Question 2Which function converts a string such as "12.5" to a floating-point number? A. str() B. float() C. int() D. bool() Correct Answer: B. float() Question 3What does input() return before any explicit conversion? A. An integer B. A Boolean C. A string D. A list Correct Answer: C. A string Question 4Which statement skips the remainder of the current loop iteration? A. break B. pass C. stop D. continue Correct Answer: D. continue Question 5Which exception is most directly associated with int("abc")? A. ValueError B. KeyError C. IndexError D. FileNotFoundError Correct Answer: A. ValueError Question 6What values are produced by range(3)? A. 1, 2, 3 B. 0, 1, 2 C. 0, 1, 2, 3 D. 3 only Correct Answer: B. 0, 1, 2 Question 7Which expression safely checks whether a variable named value is the singleton None? A. value == empty B. value = None C. value is None D. value equals None Correct Answer: C. value is None Question 8When does a loop’s else block execute? A. Whenever the loop contains a continue B. Only when the loop exits through break C. Before the first iteration D. When the loop finishes without break Correct Answer: D. When the loop finishes without break Question 9Which construct is most suitable for creating a list of squares from 0 through 5? A. [n ** 2 for n in range(6)] B. [n + 2 for n in range(5)] C. {n ** 2 in range(6)} D. range(n ** 2, 6) Correct Answer: A. [n 2 for n in range(6)]** Question 10Why does the worked measurement example test whether the list is non-empty before calculating the mean? A. To prevent division by zero B. To convert strings to integers C. To sort the measurements D. To make negative values positive Correct Answer: A. To prevent division by zero
|