Functions as first-class objects

In Python, functions are first-class objects.

First-Class functions: "A programming language is said to have first class functions if it treats functions as first-class citizens."

First-Class Citizen (Programming): "A first-class citizen (sometimes calles first-class object) is an entity which supports all the operations generally available to other entities, such as being being passes as an argument, returned from a function, and assigned to a variable."

Assign function to variables

Use function as argument in function

Return function from function

Attributes of user-defined functions

Some of these attributes return objects that have attributes themselves:

Function scoping rules

Use of global

Because a is assigned inside the function body, it is a local variable in the local namespace of the f function and a completely separate object from the variable a defined in the global namespace.

global declares the a variable created inside the function body to be a global variable, making it reassing the previously declared global variable of the same name.

Use of nonlocal:

Because n reassigned inside decrement, it is a local variable of that function's namespace. But because n -= 1 is the same as n = n - 1 and Python first runs the right hand side of assignment statements, when running n - 1 throws an error because there it is asked to reference the variable n that hasn't been assigned yet.

nonlocal declares n to be a variable in the scope of an enclosing function of decrement.

Closures

A closure is the object that results from packaging up the statements that make up a function with the environment in which it executes. David Beazley, Python Essential Reference

A closure is a function that has access to variables that are neither global nor local (i.e. not defined in the function's body). Luciano Ramalho, Fluent Python

A closure is an inner function that remembers and has access to variables in the local scope in which it was created, even after the outer function has finished executing. - 'A closure closes over the free variables from the environment in which they were creates'. - A free variable is a variable defined inside the scope of the outer function that is not an argument in the inner function. Below, the 'free variable' is message. Stanford CS course

How I think of them (not sure that's correct):

In the first case below, the inner function doesn't reference a nonlocal variable and thus isn't a closure. In the second case, it does reference a nonlocal variable, which gets stored in its closure attribute, which makes inner a closure.

Mutable function parameters

Notes based on chapter 8 in Fluent Python.

Functions that take mutable objects as arguments require caution, because function arguments are aliases for the passed arguments (i.e. they refer to the original object). This can cause unintended behaviour in two types of situations:

Setting a mutable object as default

Between bus 1 and 2, all works as intended, since we passed our own list when creating bus 1. Then things get a bit weird, though: how did Heather get into bus 3? When we define the HauntedBus class, we create a single empty list that is kept in the background and will be used whenever we instantiate a new bus without a custom passenger list. Importantly, all such buses will operate on the same list. We can see this by checking the object ids of the three buses' passenger lists:

This shows that while the passenger list of bus 1 and 2 are not the same object, the lists of bus 2 and 3 are. Once we know that, the above behaviour makes sense: all passenger list operations on buses without a custom list operate on the same list. Anohter way of seeing this by inspecting the default dict of HauntedBus after our operations abve.

The above shows that after the bus3.pick('katy') call above, the default list is now changed, and will be inherited by future instances of HauntedBus.

This behaviour is an example of why it matters whether we think of variables as boxes or labels. If we think that variables are boxes, then the above bevaviour doesn't make sense, since each passenger list would be its own box with its own content. But when we think of variables as labels -- the correct way to think about them in Python -- then the behaviour makes complete sense: each time we instantiate a bus without a custom passenger list, we create a new label -- of the form name-of-bus.passengers -- for the empty list we created when we loaded or created HauntedBus.

What to do to avoid the unwanted behaviour? The solution is to create a new empty list each time no list is provided.

Aliasing a mutable object argument inside the function

The init method of the above class copies the passed passenger list by calling list(passengers). This is critical. If, instead of copying we alias the passed list, we change lists defined outside the function that are passed as arguments, which is probably not what we want.

Again, the reason for this is that self.passengers is an alias for passengers, which is itself an alias for team, so that all operations we perfom on self.passengers are actually performed on team. The identity check below shows what the passengers attribute of bus is indeed the same object as the team list.

To summarise: unless there is a good reason for an exception, for functions that take mutable objects as arguments do the following:

  1. Create a new object each time a class is instantiated by using None as the default parameter, rather than creating the object at the time of the function definition.

  2. Make a copy of the mutable object for processing inside the function to leave the original object unchanged.

Functional programming

A programming paradigm built on the idea of composing programs of functions, rather than sequential steps of execution.

Advantages of functional programming:

Memory and speed considerations:

See here for more.

Exercises

Mapping is the process of applying a function elementwise to an array and storing the result. Apply the len function to each item in a and return a list of lengths.

Procedural approach.

List comprehension.

Functional approach.

Filtering is the process of extracting items from an iterable which satisfy a certain condition. Filter all elements of even length from a.

Procedural approach.

List comprehension.

Functional approach.

Sources