Skip to content
- Lab 05: Python Lists, Data Abstraction, Trees (24/06/22)
- Reading Ch. 2.4: Mutable Data (27-28/06/22)
- OOP:
- A metaphor for organizing large programs into modular parts that
communicate
- Special syntax that improves program composition (dot notation)
- Objects: information and processes bundled together to create
abstractions and represent the properties, interactions, and behaviors of
complex things
- Attributes: named values part of the object
- <expression>.<name>
- <expression> evalues to an object, <name> is attribute
- Methods: function-valued attributes that compute results from their
arguments and their object
- Class: a type of object, represents a kind of value
- Instances of a class: specific values of a class
- All Python values are objects. They have behavior and attributes and act
like the values they represent
- Instances of primitive built-in values (integers, floats, strings, booleans)
are immutable, lists are mutable. Their values can change over time
through mutation operations
- Inserting, sorting, reversing, slicing,… do not create new list objects
- Sharing & identity:
- Unlike immutable data, methods called on one mutable data name can affect
another name
- is & is not operators: check whether two expressions evaluate to the
identical object
- Two objects are identical when equal in value and any change to one will
be reflected in the other
- Identity (is) is a stronger condition than equality (==)
- Tuples: an immutable sequence
- Tuple literal: (<element expression>, <element expression>,…)
- Finite length, element selection
- Immutable → methods for manipulating lists are not available for tuples,
but possible to change value of a mutable element
- Multiple assignment creates a n-element tuple and unpacks it
- Dictionaries: (extension to Week 5 Notes’ entry)
- A key cannot be or contain mutable value
- Local state: changing values that have some particular content at any
time
- nonlocal statement: whenever we change the binding associated with
nonlocal, the binding is changed in the first frame in which that variable
is already found
- Without nonlocal statements, you can only access names in the parent frame
but are not able to change their values
- Non-local assignment allows for the management of a function over its
internal state, which is separate from that of other instances of the
function
- Lecture 14: Mutable Values (27/06/22)
- Mutable default arguments are dangerous, the changes made to them in the
body are kept when calling the functions multiple time
- Lecture 15: Mutable Functions (28/06/22)
- Assignment statements have many meanings:
- x = 2
- No nonlocal statement & x is not bound locally → Create a new binding
from name x to object 2 in the first frame of the current environment
- No nonlocal statement & x is bound locally → Rebind name x to object 2
in the first frame of the current environment
- Nonlocal x & x is bound in a non-local frame → Rebind name x to 2 in
the first non-local frame of the current environment in which it is bound
- Nonlocal x & x is not bound in a non-local frame → Error: no binding
for nonlocal ‘x’ found
- Nonlocal x & x is bound in a non-local frame & x also bound locally →
Error: name ‘x’ is parameter and nonlocal
- Python pre-computes which frame contains each name before executing the body
of a function
- Within the body of a function, all instances of a name must refer to the
same frame
- However, persistent local state in mutable functions can still be achieved
using mutable values, since they can be changed without a non-local
statement
- Referential transparency: expressions are referentially transparent if
substituting an expression with its value does not change the meaning of a
program
- Mutation operations violate referential transparency because they do more
than return a value; they change the environment
- Dispatch function: a function that determines which implementation of a
method to call
- The message determines the behavior of the function
- The additional arguments are used in that behavior
- Homework 04: Data Abstraction, Trees, Nonlocal (28-29/06/22)
- Reading Ch. 4.2: Implicit Sequences (30/06/22)
- All elements of a sequential dataset can be accessed by constructing an
object without computing the value of every value in advance; they are
computed on demand
- Lazy computation: describes any program that delays the computation of a
value until it is needed
- Iterator: a mutable object that provides sequential access to
values, one by one
- The iterator abstraction has two components
- A mechanism for retrieving the next element
- A mechanism for signalling the end of the sequence
- <iterator object> = iter(<iterable data type>)
- When there are no more values, Python raises a StopIteration exception
- An iterator maintains local state to represent its position in a sequence
→ Two names for the same iterator will share a position, and there can be
multiple iterators on a sequence
- iter(<iterator>) returns that iterator
- While not as flexible, sequential access to sequential data is
sufficient for data processing applications, thus random access is not
needed
- Iterable: any value that can produce iterators is called an iterable
value
- Python: sequence (strings, tuplets), containers (sets, dictionaries (keys,
values, items)), iterators (can be passed into iter())
- Sets and dictionaries are unordered, but Python guarantees certain
properties about their order
- In Python 3.6+, the order of items in a dictionary is the order in which
they were added
- If a dictionary adds or removes a key, all iterators → invalid, future
iterators → exhibit arbitary changes to content order, which won’t happen
when an existing key changes its value
- for <name> in <expression>:
- <suite>
- <expression> → iterable value
- __iter__ method is called on that value
- Until StopIteration exception is raised, Python repeated invokes
__next__ method on that iterator and binds the result to <name>
- Execute <suite>
- Generator: an iterator returned by a special class of function
called a generator function
- Generator function
- Don’t contain return statements, but rather use yield statement to return
elements of a series
- Rather than using object’s attributes to track their progress through a
series, they use yield statement to return the elements of a series
- Lecture 16: Iterators (30/06, 02/07/22)
- iter(iterable): Return an iterator over the elements of an iterable value
- next(iterator): Return the next element in an iterator
- list(iterator): Return the remaining elements at the current position and
advance the iterator’s position to the end
- All iterators are mutable → change in position without creating new copies
- Built-in functions for iteration: (all return an iterator)
- map(func, iterable) → Iterate over f(x) for x in iterable
- filter(func, iterable) → Iterate over x in iterable if func(x)
- zip(first_iter, second_iter) → Iterate over co-indexed (x, y) pairs
- reversed(sequence) → Iterace over x in sequence in reverse order
- To view the contents of an iterator, place the resulting elements into a
container:
- list(iterable) → Create a list containing all x in iterable
- tuple(iterable) → Create a tuple containing all x in iterable
- sorted(iterable) → Create a sorted list containing x in iterable
- Generator function:
- When a generator function is called, it returns a generator that
iterates over its yields
- When next() is called on the returned generator, the respective function
generator keeps executing until a yield statement is reached, at which
point the result is yielded as the next element in the generator/iterator.
The execution pauses at the yield statement but remembers all of the
environment of the function execution. The next time next() is called, it
continues where it left off
- yield from: a statement that yields all values from an iterator or
iterable
- Trick: yield from map(fn, <iterable>) to perform calculation on elements
of the input <iterable>
- Discussion 05: Data Abstraction, Trees, Mutability (16/07/22)