Skip to content

3. Common Pitfalls

Kamal Banga edited this page Aug 31, 2018 · 15 revisions

Iterators get empty after iteration

  • Iterating twice
    >>> from statistics import mean, median
    >>> cubes = (n**3 for n in range(10))
    >>> mean(nums)
    202.5
    >>> median(nums)

    StatisticsError: no median for empty data

  • Containment checking
    >>> cubes = (n**3 for n in range(10))
    >>> 125 in cubes
    True
    >>> 125 in cubes
    False

This might look unexpected behaviour 💭. Containment internally iterates over the iterable, hence the second case here is committing the same treason as the first one, i.e., iterating over an iterator twice. Iterating once exhausts an iterator and makes it empty and hence the weird behaviour.

But why does an iterator behave so?


Assignment does not copy data

a = b = [1,2]
a.append(3)

What is b? It's [1,2,3].

a = [1,2]
b = a.copy() # (shallow) copy
a.append(3)

Now, b is still [1,2].


Mutable and Immutables

Mutable default parameter value

def append5(data=[]):
  data.append(5)
  return data

Using this append5 method results in an astonishing behaviour

>>> append5()
[5]
>>> append5()
[5, 5]
>>> append5()
[5, 5, 5]

❗️❓😳What just happened?

Lesson: Don't use mutable values as default parameter values 🚫

Closures

Let's say we want a running average and we implement it using a class

class Averager:
  
  def __init__(self):
    self.series = []
  
  def __call__(self, num):
    self.series.append(num)
    return sum(self.series)/len(self.series)

This can be used as

>>> avg = Averager()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0

Same thing can be achieved using a closure

def make_averager():
  series = []
  
  def average(num):
    series.append(num)
    return sum(series)/len(series)
  
  return average

It too achieves the same purpose

>>> avg = make_averager()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0

Now, let's say we rewrite our make_averager as

def make_averager():
  count = 0
  total = 0
  
  def average(num):
    count += 1
    total += num
    return total/count
  
  return average

Trying to do the same thing leads us to UnboundLocalError

avg(10)

UnboundLocalError: local variable 'count' referenced before assignment

❗️❓😧

Direct links

Iterators

Bell Curve

Clone this wiki locally