Prepstellar

Python Fundamentals · Python Runtime and Core Syntax

32 cards

Values, Text, and First Programs

Swipe, scroll or use ← →
  1. Numbers, operators, and names

    The interpreter is a calculator before it is anything else, and typing expressions into it is the fastest way to learn how Python treats values. The operators +, -, * and / perform arithmetic, and parentheses group expressions:

    >>> 2 + 2
    4
    >>> 50 - 5 * 6
    20
    >>> (50 - 5 * 6) / 4
    5.0
    

    Notice the last result. Whole numbers such as 2, 4 and 20 have type int; numbers with a fractional part such as 5.0 and 1.6 have type float. The / operator always returns a float, even when the division comes out even.

    1 / 32
  2. Numbers, operators, and names

    Three more operators complete the arithmetic set, and each answers a different question.

    Operator What it gives Example
    / A float quotient, always 17 / 35.666666666666667
    // Floor division: the quotient with the fractional part discarded 17 // 35
    % The remainder of the division 17 % 32
    ** A power, written with two consecutive asterisks 5 ** 225

    Mixing the two numeric types is allowed. When an operator has one integer operand and one float operand, Python converts the integer operand to floating point, so the result is a float:

    >>> 4 * 3.75 - 1
    14.0
    
    2 / 32
  3. Numbers, operators, and names

    The equal sign assigns a value to a name, and after an assignment the interpreter prints nothing before the next prompt:

    >>> width = 20
    >>> height = 5 * 9
    >>> width * height
    900
    

    A name only exists once it has been assigned. Using one that was never given a value raises NameError:

    >>> n
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'n' is not defined
    

    That error is a spelling and ordering check as much as anything: it usually means a typo, or a line that runs before the line that defines the value.

    3 / 32
  4. Quick check

    Which operator always produces a floating-point result, even when the division is exact?

    1. A`%`, because a remainder is measured against a fractional divisor

      `%` returns the remainder of a division, and with two integer operands that remainder is an integer.

    2. B`**`, because raising to a power widens the value

      `**` calculates a power; with integer operands such as `5 ** 2` the result is the integer `25`.

    3. C`/`, because regular division returns a float in every case

      Right. Regular division always returns a float, which is why `(50 - 5 * 6) / 4` shows `5.0` rather than `5`.

    4 / 32

  5. Text and how to quote it

    Python represents text with the type str, and single-quoted and double-quoted literals produce exactly the same kind of value:

    >>> 'spam eggs'
    'spam eggs'
    >>> "Paris rabbit got your back :)! Yay!"
    'Paris rabbit got your back :)! Yay!'
    

    Because a quote character can also appear inside the text, there are two ways out. Escape it with a backslash, or choose the other quotation style so no escape is needed at all:

    >>> 'doesn\'t'
    "doesn't"
    >>> "doesn't"
    "doesn't"
    

    The second version is easier to read, and picking the quote style that avoids escaping is usually the better habit.

    5 / 32
  6. Text and how to quote it

    There is a difference between how a string is stored and how it is shown. Echoing a value at the prompt shows the enclosing quotes and leaves escape sequences visible. print() omits the enclosing quotes and interprets the escaped special characters, so \n becomes an actual line break:

    >>> s = 'First line.\nSecond line.'
    >>> s
    'First line.\nSecond line.'
    >>> print(s)
    First line.
    Second line.
    
    6 / 32
  7. Text and how to quote it

    Sometimes the backslashes are the data — a Windows path, a regular expression — and interpreting them would destroy the value. A raw string, written with r before the opening quote, does not treat backslash-prefixed characters as special:

    >>> print('C:\this\name')
    C:  his
    ame
    >>> print(r'C:\this\name')
    C:\this\name
    

    One restriction comes with it: a raw string may not end in an odd number of backslashes. So a raw literal is the right tool whenever the backslashes must survive intact and the text does not finish on a lone backslash.

    7 / 32
  8. Quick check

    A program needs the exact text `C:\this\name`, with both backslashes preserved, and the value does not end in a backslash. Which literal fits?

    1. AThe raw literal `r'C:\this\name'`, which leaves the backslash sequences alone

      Right. A raw string does not interpret backslash-prefixed characters, and this value does not end in a backslash, so the restriction never applies.

    2. BThe plain literal `'C:\this\name'`, written with no prefix at all

      Without the raw prefix, `\t` and `\n` are read as a tab and a newline, so the backslashes are lost from the value.

    3. CThe raw literal `r'C:\this\name\'`, trimming the final backslash afterwards

      A raw string may not end in an odd number of backslashes, so that literal is not usable in the first place.

    8 / 32

  9. Joining and repeating text

    Two operators build longer strings out of shorter ones: + concatenates and * repeats.

    >>> 3 * 'un' + 'ium'
    'unununium'
    

    There is also a quieter rule. Two or more string literals written next to each other are concatenated automatically, which is the neat way to break a long message across lines:

    >>> text = ('Put several strings within parentheses '
    ...         'to have them joined together.')
    >>> text
    'Put several strings within parentheses to have them joined together.'
    

    That shortcut works only between literals. A variable or an expression must be joined with +, so prefix 'thon' is a syntax error while prefix + 'thon' gives 'Python'.

    9 / 32
  10. Quick check

    `prefix` holds `'Py'`. Which expression correctly produces `'Python'`?

    1. A`prefix 'thon'`, since neighbouring pieces of text are always joined for you

      Automatic joining happens only between two literals; with a variable on the left this is a syntax error.

    2. B`prefix + 'thon'`, since a variable has to be joined with the plus operator

      Right. Adjacent literals concatenate on their own, but a variable or expression must be joined explicitly with `+`.

    3. C`prefix * 'thon'`, since the repetition operator glues the two parts together

      Multiplication repeats a string a whole number of times; it cannot combine two pieces of text.

    10 / 32

  11. Keep your progress in the app

    That’s 3 of 11 quick checks. In the app they stay answered, and every lesson remembers where you left off.

  12. Reaching inside a string

    Strings can be indexed, and indexing starts at zero. Negative indices count from the right, starting at -1, which saves you from computing a length just to reach the last character:

    >>> word = 'Python'
    >>> word[0]
    'P'
    >>> word[-1]
    'n'
    >>> word[-2]
    'o'
    

    Since -0 is the same as 0, negative indices necessarily begin at -1 rather than at zero.

    Slicing takes a range instead of one position, and the rule that decides every slice is: the start is included and the end is excluded.

    >>> word[0:2]
    'Py'
    >>> word[2:5]
    'tho'
    
    11 / 32
  13. Reaching inside a string

    That convention is what makes word[:2] + word[2:] rebuild the original string with nothing duplicated and nothing lost. An omitted first index defaults to zero and an omitted second index defaults to the length of the string.

    Out-of-range positions behave differently depending on what you asked for:

    Expression Result
    word[42] on a six-character string Raises IndexError — a single position must exist
    word[4:42] 'on' — the boundary is clipped, no error
    word[42:] '' — an empty string, no error

    So a stray index is reported loudly, while a slice that overshoots is simply trimmed to what is there.

    12 / 32
  14. Quick check

    For `word = 'Python'`, what does `word[2:5]` produce?

    1. A`'thon'`, because both boundaries are included in the result

      Including both edges would return four characters; the end boundary of a slice is never part of the result.

    2. B`'ho'`, because a slice drops the characters at both boundaries

      Only the end boundary is excluded; the character at the start position is always kept.

    3. C`'tho'`, because position 2 is included and position 5 is excluded

      Right. A slice runs from its included start edge up to, but not including, its end edge.

    13 / 32

  15. Strings never change

    Python strings are immutable: once built, their contents cannot be edited in place. Assigning to an indexed position therefore raises TypeError rather than quietly replacing a character:

    >>> word[0] = 'J'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'str' object does not support item assignment
    

    The supported move is to create a new string out of the pieces you want:

    >>> 'J' + word[1:]
    'Jython'
    >>> word[:2] + 'py'
    'Pypy'
    

    Nothing was edited there: two new values were built from slices and fresh text, and the original word is untouched.

    14 / 32
  16. Quick check

    Code assigns a new character to one indexed position of a string. What happens?

    1. APython raises `TypeError`, because a string cannot be changed in place

      Right. Strings are immutable, so item assignment fails; a new string has to be built from slices and text instead.

    2. BPython appends the new character at the end of the existing string

      Appending is not what indexed assignment requests, and it would not be possible on an immutable value either.

    3. CPython swaps the character in place, since a string is a mutable sequence

      Strings cannot be modified after they are created, which is exactly why the assignment fails.

    15 / 32

  17. Lists hold items, and let you change them

    A list is written as comma-separated items between square brackets. Its items may be of different types, although in practice they usually share one:

    >>> squares = [1, 4, 9, 16, 25]
    >>> squares[0]
    1
    >>> squares[-3:]
    [9, 16, 25]
    

    Lists are indexed and sliced just like strings, with one useful difference: a list slice returns a new list, not a view onto the old one.

    16 / 32
  18. Lists hold items, and let you change them

    Unlike strings, lists are mutable, and there are three distinct ways to change one:

    Operation Effect
    cubes[3] = 64 Indexed assignment replaces one item
    cubes.append(216) Adds one new item at the end
    letters[2:5] = ['C', 'D', 'E'] Slice assignment replaces a stretch of items

    Slice assignment can even change the size of the list: letters[2:5] = [] removes those items, and letters[:] = [] clears the list entirely.

    >>> cubes = [1, 8, 27, 65, 125]
    >>> cubes[3] = 64
    >>> cubes.append(216)
    >>> cubes
    [1, 8, 27, 64, 125, 216]
    
    17 / 32
  19. Quick check

    Which operation adds exactly one new item at the end of an existing list?

    1. ATaking a slice that covers the whole list

      A full slice returns a new list with the same items; it adds nothing to the original.

    2. BCalling the list's `append()` method

      Right. `append()` mutates the list by placing one new item at its end.

    3. CBinding the list to a second variable name

      Assignment gives the same list another name; it changes neither the items nor the length.

    18 / 32

  20. Two names, one list

    Simple assignment never copies data. When you assign a list to a variable, that variable refers to the existing list, so a change made through either name is visible through the other:

    >>> rgb = ["Red", "Green", "Blue"]
    >>> rgba = rgb
    >>> rgba.append("Alph")
    >>> rgb
    ['Red', 'Green', 'Blue', 'Alph']
    

    When you actually want an independent list, ask for one. Every slice operation returns a new list, so a full slice makes a shallow copy that can be edited without touching the original:

    >>> correct_rgba = rgba[:]
    >>> correct_rgba[-1] = "Alpha"
    

    Sharing is the default; copying is the deliberate act.

    19 / 32
  21. Quick check

    After `rgba = rgb`, an item is appended through `rgba`. What does `rgb` show?

    1. AOnly `rgb` changes, because the original name owns every change made

      Neither name owns the list; both simply refer to it, so the change is not attached to one of them.

    2. BNothing changes anywhere at all, because the items of a list cannot be modified

      Lists are mutable, and `append()` genuinely adds an item to the shared list.

    3. CThe appended item too, because both names refer to one and the same list

      Right. Simple assignment does not copy, so the two names refer to one list and both see the appended item.

    20 / 32

  22. A first program: state, repetition, output

    A short Fibonacci program brings together everything a program needs: values that change, a loop that repeats, and output.

    >>> a, b = 0, 1
    >>> while a < 10:
    ...     print(a)
    ...     a, b = b, a + b
    ...
    0
    1
    1
    2
    3
    5
    8
    

    The first line is a multiple assignment: a and b receive 0 and 1 at once. The same line appears inside the loop, where the order of operations matters: every expression on the right-hand side is evaluated before any assignment takes place, and those expressions are evaluated from left to right. That is what lets a, b = b, a + b use the old a while computing the new b.

    21 / 32
  23. A first program: state, repetition, output

    The while loop repeats as long as its condition remains true, and indentation is how Python groups the statements of its body. Every line of one block must be indented by the same amount.

    The condition does not have to be a comparison. Truth follows a simple rule:

    Value in a condition Treated as
    A nonzero integer True
    Zero False
    A nonempty string, list or other sequence True
    An empty sequence False

    So while count: keeps looping while count is not zero, and stops on its own when the countdown reaches zero.

    22 / 32
  24. A first program: state, repetition, output

    print() writes the values it is given. It differs from simply echoing an expression: strings are printed without quotes, and a space is inserted between multiple items.

    >>> i = 256 * 256
    >>> print('The value of i is', i)
    The value of i is 65536
    

    Its end keyword replaces the final newline with whatever you supply, which turns one value per line into a single running line:

    >>> a, b = 0, 1
    >>> while a < 1000:
    ...     print(a, end=',')
    ...     a, b = b, a + b
    ...
    0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,
    
    23 / 32
  25. Quick check

    A countdown must repeat while a positive integer remains and print each value followed by a comma instead of a line break. Which design does that?

    1. AUse an empty list as the `while` condition and call `print(value, end=' ')`

      An empty sequence is false, so that loop would never run a single iteration.

    2. BUse the integer as the `while` condition and call `print(value, end=',')`

      Right. A nonzero integer is true until it reaches zero, and the `end` keyword replaces the newline with the requested comma.

    3. CUse the integer as the `while` condition and write the value at the prompt

      Echoing an expression only shows values at the interactive prompt, and it offers no control over what ends the line.

    24 / 32

  26. What a float actually stores

    Floating-point numbers are held in hardware as base-2 fractions, and most decimal fractions cannot be represented exactly as binary fractions. The decimal floats you type are therefore stored as the nearest available binary approximation. In base 2, one tenth is an infinitely repeating fraction; stop at any finite number of bits and what remains is an approximation.

    Most people never notice, because of how values are shown. Python prints a rounded decimal form of the stored binary approximation. The true decimal value stored for 0.1 begins 0.1000000000000000055511151231257827…, which is more digits than anyone needs, so the prompt displays 0.1 instead.

    25 / 32
  27. What a float actually stores

    The display is a courtesy, not a correction. The stored value is unchanged: it is still the nearest representable binary fraction.

    String formatting can limit the digits shown — either significant digits or digits after the decimal point — and it changes only the presentation:

    >>> format(math.pi, '.12g')
    '3.14159265359'
    >>> format(math.pi, '.2f')
    '3.14'
    

    None of that alters what is in memory. Rounding the display of a final result is exactly the right move for a report, and exactly the wrong move for a comparison.

    26 / 32
  28. Quick check

    Why can Python show `0.1` when the value stored for it is not exactly one tenth?

    1. ABecause the stored value becomes exact one tenth when printed

      Printing never rewrites the stored value; only the text on screen is chosen for you.

    2. BBecause every decimal fraction is stored as a finite base-two fraction already

      Most decimal fractions have no exact finite binary form, which is the whole reason the approximation exists.

    3. CBecause what you see is a rounded decimal form of the stored binary approximation

      Right. Python displays a short rounded form of the nearest representable binary fraction it actually holds.

    27 / 32

  29. Comparing values that are only close

    One illusion begets another. Because 0.1 and 0.3 each carry their own approximation, adding 0.1 three times does not compare equal to 0.3:

    >>> 0.1 + 0.1 + 0.1 == 0.3
    False
    

    Rounding the operands first does not help, because neither 0.1 nor 0.3 can get any closer to its intended exact value than it already is:

    >>> round(0.1, 1) + round(0.1, 1) + round(0.1, 1) == round(0.3, 1)
    False
    
    28 / 32
  30. Comparing values that are only close

    The tool for the job is math.isclose(), which compares inexact values without demanding that they match bit for bit:

    >>> math.isclose(0.1 + 0.1 + 0.1, 0.3)
    True
    

    Keep the two needs separate. Formatting decides what a report shows; math.isclose() decides whether two computed results agree.

    And note what this is not: representation error is a property of binary floating-point arithmetic, not a defect in Python or in your code. The same behavior appears in every language that uses the same floating-point hardware.

    29 / 32
  31. Quick check

    A report must show a short decimal result, while a test must tolerate the representation error in `0.1 + 0.1 + 0.1`. What handles both?

    1. ARound each operand first and then compare with `==`, since the inputs become exact

      Pre-rounding cannot bring either value closer to its exact decimal, so the strict comparison still fails.

    2. BFormat the report output, and compare with `math.isclose()` in the test

      Right. Formatting controls what the report displays, and `math.isclose()` is the tool for comparing inexact results.

    3. CPrint every stored digit and compare with `==`, since no error is hidden

      Showing more digits reveals the approximation but does not remove it, so strict equality still fails.

    30 / 32

  32. Key takeaways

    • Division splits three ways: / always returns a float, // returns the floored quotient, % returns the remainder, and ** raises to a power; mixing an int with a float converts the int.
    • A name must be assigned before it is used, or Python raises NameError.
    • Quoting is a choice, escaping is a fallback: print() drops the enclosing quotes and interprets escapes, while a raw string keeps backslashes literal and may not end in an odd number of them.
    • Indexing starts at zero and slices exclude their end: a bad index raises IndexError, but an overshooting slice is trimmed silently.
    • Strings are immutable and lists are mutable: indexed assignment fails on a string but replaces an item in a list, where append() and slice assignment also work.
    • Ordinary list assignment shares one object; a full slice is what makes a copy.
    • A first program combines state, repetition and output: multiple assignment, an indented while body, and print() with its end keyword.
    • A short display hides a binary approximation, so compare inexact results with math.isclose() rather than ==.
    31 / 32
  33. Quick check

    How does Python process `a, b = b, a + b`?

    1. AIt evaluates both right-hand expressions before it assigns either name

      Right. Every right-hand expression is evaluated first, from left to right, before any name is updated.

    2. BIt assigns `a` first, then evaluates the expression for `b`

      Assigning `a` first would destroy the old value that the second expression still needs.

    3. CIt works from right to left, assigning each name as soon as its value appears

      No assignment happens part-way through; the complete right-hand side is evaluated before any name changes.

    32 / 32

  34. 11 quick checks · then the test

    In the app, finishing the quick checks opens this lesson’s 10-question test, and the ones you miss come back exactly when you’re about to forget them.

The whole course, on your phone

Lessons you can read, audio you can listen to on the way to work, and practice that remembers what you got wrong.