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 mutationoperations
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 (==)
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:
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)