ECE181Semester 1 • 2 CreditsCore Subjects

Introduction To Python

An introductory Python programming course covering core language constructs, data structures, and libraries such as NumPy, Pandas, Matplotlib, and OpenCV, culminating in real-world application projects, technical reporting, and presentations.

6 Units • Notes • Model Answers • MCQs
Select Course Unit (6 Units)
Active: Unit 1
Unit 1:Unit 1: Introduction; Variables, Expressions and Statements
7 min read•High-Yield Unit Guide

Unit 1: Introduction; Variables, Expressions and Statements

Unit 1 Exam Focus & Core Takeaways

Review the core definitions, standard architectures, and syntax patterns below. Pay special attention to sections highlighted with bullet points for your mid-term and end-term subjective questions.

Unit 1: Introduction; Variables, Expressions and Statements

1. Computational Thinking and the Python Language Philosophy

Computers are fundamentally deterministic electronic calculating machines built from billions of nanoscale transistors that manipulate binary voltages (logic 0 and 1). Because binary machine language is illegible to human cognition and prone to error, programming languages bridge human logic and electronic hardware execution.

Python, created by Dutch programmer Guido van Rossum in the late 1980s and officially released in 1991, was designed around the guiding philosophy that "readability counts." Formally codified in The Zen of Python (PEP 20), the language emphasizes clean, concise syntax, minimal boilerplate, and consistent semantic behavior:

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Readability counts.
CODE
       HUMAN PROGRAMMER                TRANSLATION ENGINE                CPU EXECUTION
     +-------------------+            +--------------------+            +--------------+
     | Python Source     |  Compile   | Python Bytecode    |  Interpret | Host CPU     |
     | File: script.py   | ---------->| File: script.pyc   | ---------->| Architecture |
     | (Readable Text)   |            | (CPython PVM)      |            | (x86 / ARM)  |
     +-------------------+            +--------------------+            +--------------+

Execution Model: The CPython Pipeline

While colloquially described as an "interpreted language," standard Python (CPython) utilizes a two-tier execution pipeline:

  1. Source Parsing and Compilation: When a script is executed, CPython parses the human-readable .py text file into an Abstract Syntax Tree (AST) and compiles it into an intermediate, platform-neutral representation known as Bytecode (.pyc files stored in __pycache__).
  2. Virtual Machine Interpretation: The compiled bytecode instructions are then executed sequentially by the Python Virtual Machine (PVM), an execution engine written in C that interprets opcodes (e.g., LOAD_CONST, BINARY_ADD, STORE_FAST) into native CPU machine instructions.
  3. Contrast with Compiled Languages (C/C++, Rust):
    • Compiled languages translate source code directly into target-specific machine binaries prior to execution. They achieve near-instant execution speed but require recompilation for every OS/CPU architecture.
    • Interpreted/Bytecode languages (Python) offer cross-platform portability and interactive REPL experimentation at the cost of interpretive execution overhead.

2. Identifiers, Keywords, and the Object Reference Model

Lexical Identifiers and Naming Rules

An identifier is a user-defined token used to name program entities such as variables, functions, classes, modules, and objects.

In Python, identifier creation is strictly governed by lexical rules:

  1. Character Set: May consist of alphanumeric characters (uppercase letters A-Z, lowercase letters a-z, digits 0-9) and the underscore character (_).
  2. Initial Character: Must begin with a letter or an underscore. An identifier can never start with a numeric digit (e.g., 2speed is illegal, whereas speed_2 is valid).
  3. Punctuation & Special Symbols: Whitespace and punctuation characters ($, @, %, -, !) are forbidden.
  4. Case Sensitivity: Identifiers are strictly case-sensitive. voltage, Voltage, and VOLTAGE represent three completely distinct memory references.
  5. Reserved Keywords: Cannot match any of Python's reserved language keywords (such as for, while, def, class, if, else, import, return, None, True, False).
python
# Valid Identifiers
sensor_reading = 42
_internal_clock = 1000
MotorSpeed2 = 180.5

# Invalid Identifiers
# 2fast = 10         --> SyntaxError: invalid decimal literal
# user-name = "Alex" --> SyntaxError: cannot assign to expression
# class = 5          --> SyntaxError: invalid syntax (class is a reserved keyword)

The Name-Binding (Object Reference) Memory Model

Unlike languages such as C or Java where a variable is a typed, fixed-size memory container into which values are stored, in Python, variables are names (tags) bound to heap-allocated objects.

CODE
   TRADITIONAL C MODEL: Fixed memory box
   [ int x ] ===> [ Memory Address 0x1040 contains integer bits: 00000101 ]

   PYTHON DYNAMIC REFERENCE MODEL: Name bound to an object
        Name 'x' --------------------+
                                     |
                                     v
                           +----------------------+
                           | int Object           |
                           | Type: <class 'int'>  |
                           | Value: 5             |
                           | Ref Count: 1         |
                           | Address: id(5)       |
                           +----------------------+
  • When you write x = 5, Python creates an integer object 5 on the heap and binds the identifier name x to that object.
  • If you subsequently write x = "Hello", Python does not force the string "Hello" into an integer memory space. Instead, it creates a new str object on the heap and re-binds the tag x to point to the new string object.
  • The built-in function id(obj) returns the unique integer memory identity of the referenced object.
  • The built-in function type(obj) returns the class type of the object.
python
a = 250
b = a
print(id(a) == id(b))  # True: both identifiers bind to the exact same object

a = 500
# 'a' is re-bound to a new integer object 500; 'b' still points to 250
print(b)               # 250

3. Fundamental Primitive Data Types

Python provides four foundational primitive scalar data types:

CODE
                  PYTHON CORE PRIMITIVE DATA TYPES
                                 |
         +-----------------------+-----------------------+
         |                                               |
      NUMERIC                                        NON-NUMERIC
         |                                               |
   +-----+-----+                                   +-----+-----+
   |           |                                   |           |
  int        float                                bool        str
(Arbitrary (IEEE-754                             (True or   (Unicode
 Precision) Double)                              False)     Text)

1. Integers (int)

  • Represent positive and negative whole numbers including zero (... -2, -1, 0, 1, 2 ...).
  • In Python 3, integers have arbitrary precision (limited only by available host RAM). There is no integer overflow; values can grow to thousands of digits without wrapping around like 32-bit/64-bit integer registers in C.
  • Integer literals can be specified in binary (0b or 0B), octal (0o or 0O), or hexadecimal (0x or 0X):
    python
    dec = 42
    bin_val = 0b101010  # 42
    hex_val = 0x2A      # 42
    

2. Floating-Point Numbers (float)

  • Represent real numbers containing decimal points or scientific exponential notation (1.25, -0.0034, 6.022e23).
  • Implemented internally using the C double type conforming to the IEEE-754 standard for binary double-precision (64-bit) floating-point arithmetic:
    • 1 sign bit
    • 11 exponent bits
    • 52 fraction/significand bits (pprox 15 ext{ to }17 significant decimal digits of precision).
  • The Floating-Point Representation Pitfall: Many base-10 fractional decimals cannot be represented exactly in binary floating-point representation:
    python
    print(0.1 + 0.2)  # 0.30000000000000004
    print(0.1 + 0.2 == 0.3)  # False!
    
    In engineering calculations, always compare floats using tolerance thresholds (math.isclose(a, b, rel_tol=1e-9)) rather than direct equality ==.

3. Booleans (bool)

  • Subclass of int representing truth values: exactly two built-in singleton instances exist: True and False.
  • In numeric evaluations, True behaves identically to integer 1, and False behaves identically to integer 0:
    python
    print(True + True)   # 2
    print(False * 100)   # 0
    print(isinstance(True, int))  # True
    

4. Strings (str)

  • Immutable sequences of Unicode characters enclosed in single quotes ('...'), double quotes ("..."), or triple quotes ('''...''' or """...""" for multi-line text).
  • Once created, individual characters within a string cannot be modified in place:
    python
    s = "Python"
    # s[0] = 'J'  --> TypeError: 'str' object does not support item assignment
    

4. Operators, Precedence, and Semantic Evaluation

An operator is a special symbol that performs specific mathematical, logical, or relational computations on one or more operands. An expression is a syntactic combination of literals, variables, and operators that evaluates to a concrete value.

CODE
+------------------------+------------------------------------+---------------------------------------+
| Category               | Operators                          | Description & Behavior                |
+------------------------+------------------------------------+---------------------------------------+
| Arithmetic             | +, -, *, /, //, %, **              | Addition, Subtraction, Multiplication,|
|                        |                                    | Float Division, Floor Division,       |
|                        |                                    | Modulus (Remainder), Exponentiation   |
+------------------------+------------------------------------+---------------------------------------+
| Comparison             | ==, !=, <, <=, >, >=               | Value Equality, Inequality, Ordering   |
+------------------------+------------------------------------+---------------------------------------+
| Identity               | is, is not                         | Memory Object Identity (id(a) == id(b))|
+------------------------+------------------------------------+---------------------------------------+
| Membership             | in, not in                         | Sequence / Collection Containment     |
+------------------------+------------------------------------+---------------------------------------+
| Logical                | and, or, not                       | Boolean Conjunction, Disjunction,     |
|                        |                                    | Inversion (with Short-Circuiting)     |
+------------------------+------------------------------------+---------------------------------------+
| Bitwise                | &, |, ^, ~, <<, >>                 | Bitwise AND, OR, XOR, NOT, Shifts     |
+------------------------+------------------------------------+---------------------------------------+
| Assignment             | =, +=, -=, *=, /=, //=, %=, **=    | Binding and Compound In-Place Mutation|
+------------------------+------------------------------------+---------------------------------------+

Deep Dive: Division Operators (/, //, %)

  1. True Division (/): Always returns a float, even when two integers divide evenly:
    python
    print(10 / 2)   # 5.0 (float)
    print(7 / 2)    # 3.5 (float)
    
  2. Floor Division (//): Divides two numbers and rounds the quotient down to the nearest mathematical integer (towards negative infinity):
    python
    print(7 // 2)    # 3
    print(-7 // 2)   # -4 (rounds toward negative infinity, NOT towards zero!)
    
  3. Modulus Operator (%): Yields the remainder of division, obeying the fundamental identity:
    extQuotient=x//yext{Quotient} = x // y
    extRemainder=xext{Remainder} = x % y = x - (x // y) imes y
    python
    print(7 % 2)     # 1
    print(-7 % 2)    # 1, because -7 - (-4 * 2) = -7 - (-8) = 1
    

Operator Precedence Hierarchy (Highest to Lowest)

When an expression contains multiple operators, evaluation order is dictated by the formal operator precedence table:

CODE
  PRECEDENCE LEVEL       OPERATOR                           ASSOCIATIVITY
  ----------------       --------------------------------   -------------
  1. Highest             ( ) Parentheses, [ ] Indexing       Left-to-right
  2.                     ** Exponentiation                   RIGHT-TO-LEFT
  3.                     +x, -x, ~x (Unary +, -, Bitwise NOT)Right-to-left
  4.                     *, /, //, %                         Left-to-right
  5.                     +, -                                Left-to-right
  6.                     <<, >>                              Left-to-right
  7.                     & (Bitwise AND)                     Left-to-right
  8.                     ^ (Bitwise XOR)                     Left-to-right
  9.                     | (Bitwise OR)                      Left-to-right
  10.                    ==, !=, <, <=, >, >=, is, in        Left-to-right (Chained)
  11.                    not                                 Right-to-left
  12.                    and                                 Left-to-right
  13. Lowest             or                                  Left-to-right

Exponentiation Right-to-Left Associativity Example

232=2(32)=29=512(NOT (23)2=82=64)2^{3^2} = 2^{(3^2)} = 2^9 = 512 \quad (\text{NOT } (2^3)^2 = 8^2 = 64)

python
result = 2 ** 3 ** 2
print(result)  # 512

Short-Circuit Logic Evaluation (and, or)

Python's logical operators and and or do not necessarily evaluate to True or False; instead, they return the exact operand that determined the truth value, evaluated strictly left-to-right:

  1. A and B: If A is falsy, Python halts evaluation immediately and returns A without evaluating B. If A is truthy, it evaluates and returns B.
  2. A or B: If A is truthy, Python halts evaluation immediately and returns A without evaluating B. If A is falsy, it evaluates and returns B.
python
# Guard pattern against ZeroDivisionError
x = 0
result = (x != 0) and (100 / x > 2)
print(result)  # False (100 / x is never evaluated, avoiding ZeroDivisionError!)

name = "" or "Default User"
print(name)    # 'Default User'

5. Type Casting, Truthiness, and Precision Rounding

Explicit Type Conversion

  • int(x): Converts valid string literals or floats to an integer. Truncates floats towards zero (int(3.9) yields 3, int(-3.9) yields -3).
  • float(x): Converts integer or valid string to 64-bit IEEE-754 float.
  • str(x): Converts any object into its standard string representation.
  • bool(x): Evaluates the "truthiness" of an object.

The Truthiness Concept

In Python, all objects can be tested for truth value inside conditional expressions. The following objects evaluate to Falsy:

  • Constants: None, False
  • Zero numeric values: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
  • Empty sequences and collections: "" (empty string), () (empty tuple), [] (empty list), {} (empty dict), set()
    All other objects evaluate to Truthy.

Rounding Mechanics: Banker's Rounding

The built-in round(number, ndigits) function implements round-half-to-even (Banker's Rounding) to eliminate statistical skew in cumulative sums:

  • When a number is equidistant between two integers (ending in .5), it rounds to the nearest even integer:
    python
    print(round(2.5))  # 2 (rounds down to even 2)
    print(round(3.5))  # 4 (rounds up to even 4)
    print(round(4.5))  # 4 (rounds down to even 4)
    
  • Contrast with math.floor() (always rounds down towards −∞-\infty) and math.ceil() (always rounds up towards +∞+\infty).

6. Formatted Console Input and Output

The input() Function

The input([prompt]) built-in function halts execution, reads a line of text entered by the user from standard input (sys.stdin), and returns it strictly as a str (string):

python
raw_age = input("Enter your age: ")  # User enters: 20
# age = raw_age + 5  --> TypeError: can only concatenate str (not "int") to str
age = int(raw_age) + 5               # Correct: explicitly cast to integer

The print() Function Parameters

print(*objects, sep=' ', end=' ', file=sys.stdout, flush=False)

  • sep: String inserted between items (defaults to a single space ' ').
  • end: String appended at the end of the line (defaults to newline ' ').
python
print("2026", "10", "15", sep="-")           # Output: 2026-10-15
print("Processing data", end="... ")
print("Done!")                               # Output: Processing data... Done!

Formatted String Literals (F-Strings)

Introduced in Python 3.6 (PEP 498), f-strings evaluate embedded expressions inside curly braces at runtime:

python
voltage = 3.1415926
current = 0.025
resistance = voltage / current

# Syntax: {expression:format_specifier}
print(f"Measured Voltage: {voltage:.2f} V")           # Measured Voltage: 3.14 V
print(f"Computed Resistance: {resistance:>10.1f} Ohms")# Computed Resistance:      125.7 Ohms
print(f"Binary Representation: {42:#010b}")          # Binary Representation: 0b00101010

7. Step-by-Step Solved Problem Walkthroughs

Problem 1: Complex Expression Precedence Evaluation

Statement: Determine the step-by-step evaluation, intermediate values, and final result of the following Python expression:

python
result = 15 - 3 * 2 ** 3 // 4 + 14 % 5

Step-by-Step Derivation:

  1. Exponentiation (**): Evaluated first with highest precedence:
    2∗∗3=82 ** 3 = 8
    Expression becomes: 15 - 3 * 8 // 4 + 14 % 5
  2. Multiplication, Floor Division, Modulus (*, //, %): These have equal precedence and evaluate left-to-right:
    • First 3 * 8 = 24
      Expression: 15 - 24 // 4 + 14 % 5
    • Next 24 // 4 = 6
      Expression: 15 - 6 + 14 % 5
    • Next 14 % 5 = 4 (since 14=2imes5+414 = 2 imes 5 + 4)
      Expression: 15 - 6 + 4
  3. Addition and Subtraction (-, +): Evaluated left-to-right:
    • 15 - 6 = 9
    • 9 + 4 = 13

Final Value: 13 (Integer).


Problem 2: Logical Short-Circuit and Truthiness Tracing

Statement: Given the following Python statements, determine the exact output printed to the console:

python
a = 0
b = 25
c = "Sensor Active"
d = []

res1 = a or b and c
res2 = d and b or "Fallback"
res3 = not a and (b > 10)
print(f"res1={res1!r}, res2={res2!r}, res3={res3!r}")

Step-by-Step Derivation:

  1. Evaluating res1 = a or b and c:
    • and has higher precedence than or.
    • First evaluate b and c: b is 25 (truthy), so b and c evaluates to c ("Sensor Active").
    • Now evaluate a or "Sensor Active": a is 0 (falsy), so or proceeds to and returns "Sensor Active".
    • res1 = "Sensor Active".
  2. Evaluating res2 = d and b or "Fallback":
    • First evaluate d and b: d is [] (empty list, falsy). Due to short-circuiting, d and b halts and returns [].
    • Now evaluate [] or "Fallback": [] is falsy, so or returns "Fallback".
    • res2 = "Fallback".
  3. Evaluating res3 = not a and (b > 10):
    • not a: a is 0 (falsy), so not 0 evaluates to True.
    • b > 10: 25 > 10 evaluates to True.
    • True and True evaluates to True.
    • res3 = True.

Output:
res1='Sensor Active', res2='Fallback', res3=True


8. Common Traps, Pitfalls, and Best Practices

  1. Confusion Between Equality (==) and Identity (is):
    • == checks value equality (whether two objects hold equivalent contents).
    • is checks memory object identity (whether two identifiers point to the identical address in memory: id(a) == id(b)).
    python
    x = [1, 2, 3]
    y = [1, 2, 3]
    print(x == y)  # True: identical contents
    print(x is y)  # False: two distinct list objects allocated on heap!
    
  2. The Mutable Default Trap & Chained Assignment:
    • Chained assignment a = b = [] binds both names a and b to the same list instance. Mutating a.append(1) will invisibly mutate b!
  3. Integer Division in Negative Numbers:
    • In Python, // rounds towards −∞-\infty. -7 // 2 is -4, NOT -3 as in C or Java.
  4. Variable Swapping Idiom:
    • Avoid temporary variable idioms (temp = a; a = b; b = temp). Use tuple unpacking:
    python
    a, b = b, a
    

9. Formula Cheat-Sheet and Quick Reference

CODE
+------------------------------------+---------------------------------------------------------------+
| Construct / Rule                   | Syntax & Behavior                                             |
+------------------------------------+---------------------------------------------------------------+
| Floor Division Formula             | q = x // y  (Quotient rounded towards -infinity)             |
| Modulus Remainder Formula          | r = x % y = x - (x // y) * y                                  |
| Exponentiation Associativity       | a ** b ** c  ==  a ** (b ** c)  (Evaluated Right-to-Left)     |
| Short-Circuit Conjunction          | a and b  (Returns 'a' if falsy; else returns 'b')             |
| Short-Circuit Disjunction          | a or b   (Returns 'a' if truthy; else returns 'b')            |
| Banker's Rounding                  | round(2.5) -> 2 ; round(3.5) -> 4 (Rounds to nearest even)    |
| Multiple Assignment Unpacking      | x, y, z = 1, 2, 3                                             |
| Tuple Value Swapping               | a, b = b, a                                                   |
| Memory Identity Inspection         | id(object)  (Unique integer memory address in CPython)        |
| Class Type Verification            | isinstance(obj, ClassInfo)  (Preferred over type(obj) == Cls) |
| F-String Alignment & Precision     | f"{val:>10.2f}"  (Right-align 10 width, 2 decimal places)     |
+------------------------------------+---------------------------------------------------------------+