post / 2023.06.27
Python Basics
This article merges four earlier Python notes, removes duplicated and irrelevant material, and keeps only what is useful as a day-to-day reference. Examples target Python 3.
Variables and Objects
- Variables need no type declaration, but they must be assigned before use, otherwise a
NameErroris raised. - Python has no
constand no mechanism preventing reassignment; a “constant” is only a naming convention (all caps). =assigns;==compares.
>>> age
NameError: name 'age' is not defined
A useful mental model: objects are the data, variables are labels stuck onto objects.
a = "Jack" # create the string object "Jack", then stick the label a onto it
b = a # stick label b onto the same object; this is not a copy
id(a) == id(b) # True, both refer to the same object
a = "Ross" # a different object gets the label a; b still refers to "Jack"
- An object can carry many labels, but a variable refers to exactly one object at a time.
- The variable itself has no type; the type belongs to the object. So
a = 1followed bya = "1"is perfectly legal.
Assignment and Slicing
= is right-associative, so a = b = c = 1 makes all three names refer to the integer object 1.
a, b, c = 1, 2, 3 # unpacking assignment
age = 17 + 1 # evaluate the right side first
Slicing works on strings, tuples, and lists with the syntax obj[a:b:c]:
| Form | Meaning |
|---|---|
obj[a:b] | Indices in $[a, b)$, excluding b; omit a to start at 0, omit b to run to the end |
obj[a:b:c] | Take every c-th element; c defaults to 1 |
negative c | Walk backwards |
obj[::-1] | Reverse (returns a new object; the original is unchanged) |
>>> name = "polaris"
>>> name[1:3]
'ol'
>>> name[::-3]
'sap'
>>> name[::-1]
'siralop'
Slicing also copies: y = x[:] gives a shallow copy, so sorting y does not affect x.
Strings
- Single and double quotes are equivalent; there is no separate single-character type.
- Triple quotes allow multi-line strings.
- To include the same quote character inside a string, escape it:
\".
Three ways to format
name, age = "Bronya", 18
print('My wife is %s, she is %d.' % (name, age)) # % formatting, the oldest
print('My wife is {}, she is {}.'.format(name, age)) # .format()
print(f'My wife is {name}, she is {age}.') # f-string, recommended
%formatting takes a tuple or dict and substitutes positionally or by name.- In
.format(),{}may use positional indices{0}or keywords{name}. - f-strings require Python 3.6+ and are the most readable; all three support width, alignment, and precision specifiers.
Numbers and Booleans
| Type | Notes |
|---|---|
int | Arbitrary-precision integer; supports 0x hex, 0o octal, 0b binary literals |
float | Floating point; scientific notation such as 1.23e-6 |
complex | Complex numbers, written a + bj or complex(a, b) |
bool | True is really 1 and False is really 0 |
NoneType | The null value; None must be capitalised |
- Mixing an int with a float promotes the int:
1 + 2.0gives3.0. - Floats carry rounding error:
0.1 + 0.2 == 0.3isFalse(the actual value is0.30000000000000004). Use thedecimalmodule when exact arithmetic is required. round()uses banker’s rounding (round-half-to-even):round(0.5)is0,round(1.5)is2,round(2.5)is2.
Falsy values: 0, 0.0, None, empty string, empty list, empty tuple, empty dict, empty set, False.
Everything else is truthy, including any non-empty string (even the string "False").
Operators
Arithmetic
| Operator | Meaning | Example |
|---|---|---|
+ - * | addition, subtraction, multiplication | |
/ | true division; the result is always a float | 6 / 3 → 2.0 |
// | floor division, rounds down | 7 // 2 → 3, -7 // 2 → -4 |
% | modulo (remainder) | 7 % 2 → 1 |
** | power, right-associative | 2 ** 2 ** 3 → 256 |
- | unary minus, binds weaker than ** | -2 ** 2 → -4 |
The rest
| Category | Operators |
|---|---|
| Comparison | == != < <= > >=; can be chained: a > b == c means a > b and b == c |
| Logical | and or not |
| Membership | in not in |
| Identity | is is not; compares whether two names refer to the same object (compares id, not value) |
| Bitwise | & | ^ ~ << >> |
| Augmented assignment | += -= *= //= **= &= |= ^= <<= >>= |
and / or return one of their operands, not True / False:
>>> True and 0 + 3
3
>>> False or 31
31
>>> 0 or 0
0
Precedence (highest to lowest)
() → indexing [] → attribute . → ** → unary ~ + - → * / % // → + - → << >> → & → ^ → | → comparisons (<, ==, in, is, all one level) → not → and → or → assignment
If you cannot remember it, just add parentheses; that is the cheapest fix.
Input, Output, and Built-in Functions
print(self, *args, sep=' ', end='\n', file=None) # sep between items, end after the last
age = input("Enter your age: ") # the return value is always a string
int(age) # convert explicitly when you need a number
Common built-ins:
| Function | Purpose |
|---|---|
id(x) | memory address (identity) of the object |
type(x) | the object’s type, e.g. <class 'int'> |
len(x) | number of elements |
abs() round() bool() | absolute value, rounding, boolean conversion |
enumerate(seq, start=0) | index and value together while looping |
range() zip() sum() max() min() sorted() | common sequence helpers |
Type-conversion functions: int(x, base), float(), complex(), str(), repr(), chr(), ord(), hex(), oct(), eval(), list(), tuple(), set(), frozenset(), dict().
Conversion is not unconditional; it depends on whether the source data carries enough information to express the target type. int("123") works, int("Hello") raises an error; a list can become a set, but not a single number.
Implicit conversion follows a precision order: bool < int < float < complex, moving toward the more precise type in mixed expressions.
galgames = ['Cafe', 'Senren Banka', 'Tenshi Sou Sou']
for index, galgame in enumerate(galgames, 1):
print(f'Playing game number {index}: {galgame}')
Conditionals and Loops
if condition1:
...
elif condition2:
...
else:
...
for iterates over anything iterable (lists, dicts, strings, tuples, generators, …):
for x in sequence:
...
for i in range(5):
...
breakleaves only the innermost loop.continueskips the rest of this iteration and goes to the next.for ... else/while ... else: theelseruns when the loop completes normally; it is skipped if the loop was left viabreak.
while condition:
...
When writing a while, always confirm that the body eventually changes the condition or triggers a break; otherwise you have an infinite loop.
The Four Container Types
| Container | Literal | Ordered | Mutable | Duplicates | Element requirement |
|---|---|---|---|---|---|
| list | [1, 2] | yes | yes | yes | any |
| tuple | (1, 2) | yes | no | yes | any |
| dict | {k: v} | insertion order | yes | keys unique | keys must be hashable |
| set | {1, 2} | no | yes | no (auto-deduplicated) | must be hashable |
list
a = [] # empty list
a.append(x) # append to the end
a.insert(i, x) # insert at index i, shifting later elements right
a.extend(b) # append each element of b
a.pop() # remove and return the last element
a.pop(i) # remove and return the element at index i
a.remove(x) # remove the first element equal to x; errors if absent
a.index(x) # index of the first element equal to x
a.count(x) # how many elements equal x
a.reverse() # reverse in place
a.sort(key=..., reverse=False) # sort in place; returns None
del a[i] # delete the element at index i
del a[1:3] # delete a slice
Sorting example:
random = [(2, 2), (3, 4), (4, 1), (1, 3)]
random.sort(key=lambda e: e[1]) # sort by the second element of each tuple
# [(4, 1), (2, 2), (1, 3), (3, 4)]
Note:
sort()accepts onlykeyandreverse. The Python 2cmpparameter was removed.
tuple
- Tuples are immutable: no insert, update, or delete.
- A one-element tuple needs a trailing comma:
(1,)is a tuple,(1)is just the integer. - Conversion:
list(t)yields a list,tuple(l)yields a tuple. The original object is unchanged; a new object is returned.
dict
profile = {'name': 'Guzheng', 'age': 19}
profile2 = dict(name='Guzheng', age=19) # with = the keys are unquoted
profile3 = dict([('name', 'Guzheng'), ('age', 19)]) # built from key-value pairs
profile4 = {i: i ** 2 for i in range(2, 5)} # {2: 4, 3: 9, 4: 16}
Access and update:
profile['name'] # raises KeyError if the key is absent
profile.get('gender') # returns None if absent
profile.get('gender', 'male') # returns a default if absent
profile.setdefault('gender', 'male') # writes the pair if absent, then returns it
profile['gender'] = 'male' # add or update
del profile['age']
profile.pop('age')
The key distinction:
get()is read-only and never modifies the dict; onlysetdefault()writes when the key is missing.
Test for a key with in / not in. has_key() is the Python 2 spelling and was removed in Python 3.
set
s = {1, 2, 2, 3} # deduplicated automatically to {1, 2, 3}
s = set() # the only way to write an empty set; {} is an empty dict
| Operation | Method | Operator |
|---|---|---|
| union | a.union(b) | a | b |
| intersection | a.intersection(b) | a & b |
| difference (in a, not in b) | a.difference(b) | a - b |
| symmetric difference | a.symmetric_difference(b) | a ^ b |
- Add:
add(x)(only hashable/immutable values),update(seq)(accepts a string, list, tuple, or set; given a dict it takes only the keys). - Remove:
remove(x)errors if absent;discard(x)does not;pop()removes and returns an arbitrary element and takes no argument;clear()empties the set. - Test:
x in s,a.isdisjoint(b)(True when they share nothing),b.issubset(a). - Sets are unordered and unindexed: you cannot read or modify elements by position, only add and remove.
Common misconception:
pop()removes an arbitrary element, not a random one. Storage order follows hash values, so it looks random but is not under your control.
Iterators and Generators
Iterable: anything you can loop over with for. It qualifies if either holds:
- it implements
__iter__()returning an iterator instance; - it has no
__iter__()but implements__getitem__()and supports indexing from 0 upward.
Iterator: what iter(iterable) returns. Call next() repeatedly to pull values; when exhausted it raises StopIteration.
from collections.abc import Iterable, Iterator
isinstance([], Iterable) # True, a list is iterable
isinstance([], Iterator) # False, but a list is not itself an iterator
isinstance(iter([]), Iterator) # True
In other words, a list is iterable but not an iterator: a list can be traversed repeatedly, while an iterator moves forward only once.
A generator is “an iterator written as a function”:
# list comprehension produces a list
alist = [i for i in range(5)] # [0, 1, 2, 3, 4]
# parentheses produce a generator
agen = (i for i in range(5)) # <generator object ...>, lazily evaluated
Note:
(i for i in ...)is a generator expression, not a tuple comprehension. Python has no tuple comprehension. To get a tuple, writetuple(i for i in ...).
yield versus return: when execution reaches yield, the function pauses and hands out a value without ending; the next request resumes right after the pause, until the function finishes. A bare yield produces None.
def generator():
i = 0
while i < 5:
i += 1
yield i
gen = generator()
next(gen) # 1
for v in generator():
print(v) # 1 2 3 4 5
The value of a generator is laziness: it never computes every result at once, so it uses far less memory than a list when handling large data streams.
Other Notes
Comments
# a single-line comment
'''
Triple quotes are technically a "multi-line string", not a comment:
the interpreter really does create a string object, it is simply an unused expression.
'''
Because it is a string rather than a comment, dropping triple quotes into the middle of code breaks the syntax and often raises an error. The correct habit is to use # for comments.
Source encoding
- Python 3 source files are UTF-8 by default, so you normally do not need an encoding declaration.
- Only Python 2 required a header line, which is now essentially obsolete:
# -*- coding: UTF-8 -*-
Identifiers
- Composed of letters, digits, and underscores, cannot start with a digit, and are case-sensitive.
- Python 3 permits non-ASCII (including Chinese) names, but this is discouraged.
- Underscore conventions:
_name: single leading underscore, meaning “internal use”;from module import *will not import it.__age: double leading underscore, triggering name mangling to avoid clashes in subclasses.__init__: leading and trailing double underscores mark a built-in special method; do not invent such names yourself.
Sponsor / I took a taxi home for sixty or seventy dollars!


When will I have a drink and discuss the details again?