Skip to content

Week 6 Notes

  • 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)