decorators-tutorial
Yet another decorators tutorial
Motivation :
Decoraters are essentially allow us to modify the function we are decorating without changing the source of that function. They are usually used to augument the functions being decorated. Kinda like what would you do with aspect-oriented programming in Java
Very handy to enforce the open-close principle, Other uses include type-checking, making a random class a single-ton at will
some more examples here : http://stackoverflow.com/questions/489720/what-are-some-common-uses-for-python-decorators?rq=1
The Basics :
Closures :
In Python, functions are first class objects, Therefore they can be passed around as arguments and be returned from other functions. functions which do that are called higher-order functions.
Whenever a function is created, a local scope/name-space is created for that function. In that you can access the local variable of that functions. The arguments passed to the function are also avalaible in the local scope. Standard scope lookup rules apply ( LEGB )
that scope is destroyed as soon as the function finishes it's execution.
Inner functions :
We can also define a function inside another function. such functions are known as inner functions
Static/Lexical scoping
The scope in python is lexical not dynamic. That is the variables resolution, when done using LEGB rules will be done on the basis of how the function was written . That is if foo is defined in bar and foo is called in fizz, then bar will be the enclosing scope for foo. fizz will not be the enclosing scope.
Closures continued
If we return a inner function, that function has access to the variables defined in it's enclosing scope. This has an important implication. This means that the scope of the enclosing function does not die immeadetly if returns an inner function. This is nothing but a closure. This is one of the reasons as to why we can call python a functional language
__call__
magic method :
The In python if we have an object foo and we call it using this syntax :
foo()
It is usefull to think of it as a short hand for foo.__call__()
. In other words any object that has implemented this method is callable like a function. For example class-objects in python implement this method. That is how we are able to get a new
instance of a class when we simply do this :
instance = MyClass()
*args
and **kargs
:
Python provides us a way to pass multiple arguments to a function. it uses two operators *
and **
. The former is used to pass the arguments by position and the latter by key-word arguments. By convention we use *args
for postion parameters and **kwargs
for key-word arguments.
We can also use *
and **
as a sort of "spread" operators, that is if we have a tuple t
we can expand it as positional params on foo : foo(*t)
. The same goes for a dict d, whose name and key-value pairs can be expanded as key word arguments
foo(**d)
Decorators :
Decorators in python, in its most simple case are a syntax sugar for this :
@foo def bar(): bar
is equivalent to
bar = foo(bar)
essentially a higher-order function returning another function. To be more precise one callable object returning another callable object
To go in depth @
desugars the decorator in 2 flavours depending on wether arguments are passed or not.
-
- The decorator function
-
- The decorator factory.
Note :
In the wild we generally find that functions and classes are used to decorate other functions and classes. the semantics for the combinations there in are slightly different.