Closures, methods, static methods, class methods

Everything you ever wondered about closures, and a few things more:

Closures: just what are closures?

Lets start with an example, and then discuss how it works.


def get_plus(increase):
    def plus(start):
        return start + increase
    return plus
... add6 = get_plus(6)
... add6(10)
16
... add8 = get_plus(8)
... add8(10)
18
... add8 is add6
False

The function get_plus returns the plus function.  It (get_plus) does not call plus(), but just returns the function plus without calling the code in plus.  When a program calls get_plus, at runtime a ‘stackframe’ is created for the get_plus function​. This stackframe will hold the parameters for get_plus together with any local variables of get_plus. In this case, the only local data is the parameter ‘increase’, but there could be any number of local variables and parameters… anything in the scope of get_plus is part of the environment of get_plus.

The ‘plus’ returned by get_plus, is actually not just  ‘plus’ but plus together with the stack frame, or local environment of get_plus, at the time plus is returned. This ‘code’ + environment is what is called a closure.

So get_plus(6) returns a copy of plus where increase in the environment is 6. Also get_plus(8) would return a copy of plus where increase in the environment is 8. Even though the code is the same, the environment is different for each plus, so the two calls do return something different.  A different closure, where a closure is the combination of function and environment the function can access. Thus add8 is add6 returns False, and the two closures returned are not the same.

Closures as code, packaged with Parameters

The plus function adds two numbers, start and increase. These can be considered the two parameters to plus, one from the parameter list, the other from the surrounding scope.  Every call to get_plus(), returns a copy of plus,  packaged together with a value for increase .  The same plus code used every time, but prepacked each time with a value for increase.

The equivalent example is provided in the page ‘closures’ where the wider use of closures is discussed as this page now focuses on how methods in kotlin and python act as closures.

 

Methods as closures: Python.

Consider the following example


class Adder:
    def __init__(self, increase):
        self.increase = increase
    def plus(self, start):
        return self.increase + start

... plus6 = Adder(6).plus
... plus8 = Adder(8).plus
... plus6(10)
16
... plus6 is plus8
False

The above code shows how a class can provide a very similar solution to the prior closure example. A method of an instanced object behaves exactly in the manner of a closure, with ‘self’ packaged in the environment of the method.  When calling ‘plus6’, there is no need to supply the object created by Adder(6), because plus6 has this self in its environment.

Also note that for two objects of the same class, the methods are actually the same.  The is test gives false when comparing the plus method of Adder(6) with the plus method of Adder(8).  Each object has its own plus, not all sharing the same plus method.  That is because each plus is actually a closure with the self of the relevant object,

Now consider the types.


// using class Adder from previous example.
... add6 = Adder(6)
... type(add6.plus)
{class 'method'}
... type(Adder.plus)
{class 'function'}
... Adder.plus(add6,8)
14

The ‘plus’ attribute of the ‘​add6‘ instance of ‘Adder’ is a method, but the attribute of class ‘Adder’ itself is a function.  In fact, within the Adder class, ‘plus‘  is just a regular function, and can be called when provided regular parameters including with an object for self as in the example with ‘Adder.plus(add6, 8)‘.  Each time an Adder object is created, that object gets its own closure of plus with bound with self for that object, and these closures are called methods.

Methods as Closures: Kotlin

In Kotlin, methods also function as closures, but in a different way.  The code in a python method accesses the object through the ‘self‘ object, in the same way code outside the class could instance an object of the class, call that object self, and access the attributes of the class. Kotlin code does not access attributes through an object, and ‘this‘ when used within the class is a name qualifier, ‘this.plus‘ signals does not signal attribute access using this as an object.  All data for the instance of the object is within name scope.  The instance of the object is the closure environment.  Defining a function within a class is just like defining a function within a function.  For a function within a function, all attributes from the scope of the outer function are available to the inner function.  A method in a class is just like that outer function.

So in Python, all attributes of the instance are available indirectly through the ‘self‘ object being in the closure environment, in Kotlin, all attributes are available directly as they are all in the closure environment.

Static Methods.

In python, static methods are simply functions that although declared within the class, are simply functions, and like regular functions, do not have any closure.  Kotlin does not have static methods, and instead uses

Consider a static method added our python Adder class.

 

class Adder:
    def __init__(self, change):
       self.change = change
    def plus(self, start):
       return start + self.change

    @staticmethod
    def minus(self, minus):
       return start- self.change

... add6 = Adder(6)
... type(add6.minus)
{ class 'function' }
... add6.minus is Adder.minus
True
... add6.minus(add6, 10)
4

The above code shows the following:

  • the static function remains a function, and does not generate a method
  • the static function is the same as the function in the class level object, and would be the exact function for every instance of Adder
  • an instanced object can be passed as a parameter to the static function, just pass it as any normal parameter

Class level methods / companion object methods

Python and Kotlin class level objects are already covered here, but the thing to add for completeness on this page is that Python Class Methods provide a closure on the class level object itself, allowing indirect access to all other class level attributes, and Kotlin companion object methods execute in the closure of the companion object itself, allowing direct access to all companion object attributes.

 

Advertisement

1 thought on “Closures, methods, static methods, class methods”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s