Python Functions are First-class Objects
In Python functions are first-class objects which means that functions can be assigned to variables or passed as an argument for another function in the same manner as primitive data types and objects. For example, in the following code, the function
operator has three parameters
y can be primitive data types,
operation must be a function. Inside of
operator the arguments
y are passed to
operation, whose result is then returned:
1 2 3 4 5 6 7 8 9 10 11 12 def operator(operation, x, y): return operation(x, y) def add(x, y): return x+y def sub(x, y): return x-y if __name__ == "__main__": print(operator(add, 23, 42)) print(operator(sub, 23, 42))
1 2 65 -19
__main__ (check out this article over here if you want to know why you should always use
__name__ == "__main__") the functions
sub are passed as
operator and those two carry out addition or subtraction when called inside of
operator. This concept of passing functions as arguments to other functions is also called higher-order functions which allows us to embed Python functions into other functions.
What is a Decorator in Python
The first-class nature of Python functions gives us the ability to encapsulate a function inside another function:
1 2 3 4 5 6 7 8 9 10 11 12 def succ(function): def wrapper(*args): result = function(*args) return result+1 return wrapper def add(x, y): return x+y if __name__ == "__main__": add_plus_one = succ(add) print(add_plus_one(23, 42))
In the code above, the
succ function takes a function as an argument and then calls that function and adds one to the result, hence the name
succ meaning successor function. In the
add is passed to
succ to construct a new function
add_plus_one, which adds two numbers and adds an additional one to the addition.
Instead of passing a function to a function to get a new function, we can use decorators as a shortcut:
1 2 3 4 5 6 7 8 9 10 11 12 def succ(function): def wrapper(*args): result = function(*args) return result+1 return wrapper @succ def add(x, y): return x+y if __name__ == "__main__": print(add(23, 42))
add does exactly the same thing as
succ(add) but a lot cleaner and globally.
When you have a class and want a method from that class without instantiating an object, you are looking for a static method. In Java or C++ one would declare a function as static with the
static keyword. In Python however, we use the decorator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Accumulator: def __init__(self): self.acc = 0 def add(self, x): self.acc += x @staticmethod def sub(x,y): return x-y if __name__ == "__main__": a = Accumulator() a.add(23) a.add(42) print(a.acc) print(Accumulator.sub(23, 42))
1 2 65 -19
In the class
Accumulator above, a value passed to
add is added to the variable
self.acc, which is only instantiated when an object of
Accumulator is created. However, our class might have useful methods that don’t depend on object attributes, such as the
sub method, which only subtracts two numbers from each other that are passed as arguments and therefore does not access data in
self. To make
sub usable without instantiating an object of
sub is decorated with
@staticmethod and is missing the parameter
self. In the
__main__ section, we see that
sub is called, putting the class name in front of it.
A Python static method knows nothing about the class it is part of and works only with its parameters.
Python classes can have attributes and methods such as objects. A class attribute can be helpful if all objects share a common value, while class methods allow us to manipulate class attributes. For example, it might be helpful to track how many instances of a class have been created:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Accumulator: count = 0 def __init__(self): Accumulator.increase_count() self.acc = 0 def add(self, x): self.acc += x @classmethod def increase_count(cls): cls.count += 1 @classmethod def get_count(cls): return cls.count if __name__ == "__main__": a = Accumulator() print(Accumulator.get_count()) b = Accumulator() print(Accumulator.get_count())
1 2 1 2
The code above
Accumulator has the class attribute
count and the class methods
get_count, which are both decorated with
@classmethod. Every time a new
Accumulator object is created,
Accumulator.increase_count, which adds one to
count and thereby tracks how many
Accumulator objects have been instantiated. Looking closely at the class methods, we can see they have the
cls parameter, representing the
Accumulator class, and there is no
self parameter. Under
__main__, we can see that every time a new object is created, the
count is increased by one, which is an attribute of the class and not the individual objects.
A Python class method can access and manipulate the attributes of a class and its first parameter is always the class itself.
This article looked at the difference between a static and a class method in Python. While static methods know nothing about the class, class methods can access and manipulate class attributes. The decorators
@classmethod are used to declare a static or class method in Python and allow you to use functions of a class without instantiating an object of that class.