It's an old article, but it was a good article to grasp the concept of Python decorators, so I translated it Japanese </ del>.
http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/
>>> def foo():
... return 1
...
>>> foo()
1
This is basic. In Python, a function can be defined with a function name and a list of parameters (optional) with the def keyword, and can be executed by specifying the name in parentheses.
In Python, creating a function creates a new scope, in other words, each function has its own namespace.
Python also provides a built-in function that allows you to check these, and locals () returns the values in your local namespace in dictionary format.
>>> def foo(arg):
... x = 10
... print locals()
...
>>> foo(20)
{'x': 10, 'arg': 20}
You can also check the global namespace with globals () in the same way.
>>> y = 30
>>> globals()
{..., 'y': 30} #Other global variables automatically created by Python are displayed, but omitted
Variable resolution rules in Python
--A new variable is always created in that namespace when it is created --References search within the same namespace, otherwise expand the search outwards
Let's look at variables in global scope in the local scope of the function.
>>> text = "I am global!"
>>> def foo():
... print text #1
...
>>> foo()
I am global!
The contents of the text defined outside the function are displayed properly. \ # 1 first looks for a local variable inside the function, and since it does not exist, it looks for a global variable named the same text.
Now let's change the variables defined outside the function inside the function.
>>> text = "I am global!"
>>> def foo():
... text = "I am local!" #2
... print locals()
...
>>> foo()
{'text': 'I am local!'}
>>> text
'I am global!'
When foo () is called, the value assigned in the function is set as the content of text, but the value of text of the outer global variable has not changed. \ # 2 actually looks for the global variable Instead of going to, a new local variable text is created in the function foo.
In other words, inside a function, global variables can be referenced but not assigned.
You need to know not only the scope but also the lifetime.
>>> def foo():
... x = 1
...
>>> foo()
>>> print x #1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
It's not just the scope problem mentioned above that is causing the error here. The namespace is created each time the function foo is called and disappears after processing. That is, in the above example, at the timing of \ # 1. It means that the variable x literally doesn't exist anywhere.
In Python you can give arguments to functions. The name of the parameter at definition is used as the name of the local variable.
>>> def foo(x):
... print locals()
>>> foo(1)
{'x': 1} #Parameter x is used as the local variable name
Python offers a variety of ways to set parameters in a function and give arguments at call time. Parameters include required parameters and optional default parameters.
>>> def foo(x, y=0): # 1
... return x - y
>>> foo(3, 1) # 2
2
>>> foo(3) # 3
3
>>> foo() # 4
Traceback (most recent call last):
...
TypeError: foo() takes at least 1 argument (0 given)
>>> foo(y=1, x=3) # 5
2
In the example of \ # 1, x is the parameter and y, which specifies the default value, is the default parameter. \ # 2 is a normal function usage example, but the default parameter is omitted as in # 3. It is also possible. The omitted argument takes the default value (0 in this case).
You can also use named arguments for Python, so you can specify the arguments without worrying about the order by specifying the name (\ # 4). If you understand that the arguments in Python are just dictionaries. It's a convincing move.
In Python you can define functions by nesting inside them.
>>> def outer():
... x = 1
... def inner():
... print x # 1
... inner() # 2
...
>>> outer()
1
The rules are the same as we've seen so far: \ # 1 looks for the local variable x but can't find it, so it looks for the outer namespace and refers to the x defined inside the outer, and \ # 2 So, we are calling inner (), but the important thing here is that ** inner is just one of the variable names, and it is called by looking for the definition in the namespace in outer based on the resolution rule ** about it.
In Python, the function itself is also just an object.
>>> issubclass(int, object) #All objects in Python are created by inheriting the same common class
True
>>> def foo():
... pass
>>> foo.__class__
<type 'function'>
>>> issubclass(foo.__class__, object)
True
What this means is that you can treat a function as if it were any other general variable-that is, you can give it as an argument to another function or use it as a return value for a function.
>>> def add(x, y):
... return x + y
>>> def sub(x, y):
... return x - y
>>> def apply(func, x, y):
... return func(x, y)
>>> apply(add, 2, 1)
3
>>> apply(sub, 2, 1)
1
In the above example, the function apply is designed to return the execution result of the function specified as a parameter, and you can see that the functions add and sub are given as arguments (just like other variables). Masu.
What about the following example?
>>> def outer():
... def inner():
... print "Inside inner"
... return inner # 1
...
>>> foo = outer() #2
>>> foo
<function inner at 0x...>
>>> foo()
Inside inner
The difference from the apply example above is that \ # 1 specifies the function itself as the return value, not the execution result (given inner instead of inner ()).
You can see that this can be assigned normally, like \ # 2, and that foo contains a function that can be executed.
Let's change the above example a little.
>>> def outer():
... x = 1
... def inner():
... print x
... return inner
>>> foo = outer()
>>> foo.func_closure
(<cell at 0x...: int object at 0x...>,)
inner is a function returned by outer, stored in foo and executable by foo () ... really?
I follow Python's variable resolution rules perfectly, but what about the life cycle? The variable x exists only while the outer function is being executed. Here, since the inner function is assigned to foo after the processing of the outer function is finished, isn't it possible to execute foo ()?
… Contrary to this expectation, foo () is feasible. This is because Python has the function of Function closure, which means that a function defined outside the __global scope (in this case, inner) remembers the information of the scope that surrounds it "at definition". You can check that it is actually remembered by calling the func_closure property as above.
Recall that we wrote "at definition". The inner function is newly defined each time the outer function is called.
>>> def outer(x):
... def inner():
... print x
... return inner
>>> print1 = outer(1)
>>> print2 = outer(2)
>>> print1()
1
>>> print2()
2
In the above example, even if you do not directly enter a value as an argument to print1 or print2, each internal inner function remembers what value should be output. This is used to take a fixed argument. You can also generate a customized function like this.
It's finally time to talk about decorators. Based on the story so far, the conclusion is that a decorator is a "callable (*) that takes a function as an argument and returns a new function in exchange."
>>> def outer(some_func):
... def inner():
... print "before some_func"
... ret = some_func() #1
... return ret + 1
... return inner
>>> def foo():
... return 1
>>> decorated = outer(foo) #2
>>> decorated()
before some_func
2
Let's understand one by one. Here we define a function called outer that takes some_func as a parameter, and in outer we also define an internal function called inner. inner gets the value to return with \ # 1 after printing the string. Some_func can take different values each time outer is called, but here we just call whatever it is , Returns the value obtained by adding 1 to the execution result. Finally, the outer function returns the inner function itself.
In \ # 2, the return value of executing outer with foo as an argument is stored in the variable decorated as some_func. When foo is executed, 1 is returned, but in decorated with outer, 1 is added to it and 2 is returned. Decorated is, so to speak, a decoration version of foo (foo + processing something).
When you actually use a useful decorator, you often replace foo itself without preparing another variable like decorated, that is, as shown below.
>>> foo = outer(foo)
The original foo will no longer be called, and the decorated foo will always return. Let's look at a more practical example.
Suppose you're using a library that holds an object at a certain coordinate. That object holds x and y coordinate pairs, but unfortunately it doesn't have the ability to handle numbers such as addition and subtraction. But we Suppose that you need to do a lot of computation with this library, and you don't want to reorganize the source of the library. What should I do?
The approach is to create functions like add and sub as shown below.
>>> class Coordinate(object):
... def __init__(self, x, y):
... self.x = x
... self.y = y
... def __repr__(self):
... return "Coord: " + str(self.__dict__)
>>> def add(a, b):
... return Coordinate(a.x + b.x, a.y + b.y)
>>> def sub(a, b):
... return Coordinate(a.x - b.x, a.y - b.y)
>>> one = Coordinate(100, 200)
>>> two = Coordinate(300, 200)
>>> add(one, two)
Coord: {'y': 400, 'x': 400}
What if, for example, a check process such as "0 must be the lower limit of the coordinate system to be handled" is required? In other words, at present
>>> one = Coordinate(100, 200)
>>> two = Coordinate(300, 200)
>>> three = Coordinate(-100, -100)
>>> sub(one, two)
Coord: {'y': 0, 'x': -200}
>>> add(one, three)
Coord: {'y': 100, 'x': 0}
However, sub (one, two) wants to return (0, 0), and add (one, three) wants to return (100, 200). Check the lower limit of each function. It's possible, but let's use a decorator to unify the check process!
>>> def wrapper(func):
... def checker(a, b):
... if a.x < 0 or a.y < 0:
... a = Coordinate(a.x if a.x > 0 else 0, a.y if a.y > 0 else 0)
... if b.x < 0 or b.y < 0:
... b = Coordinate(b.x if b.x > 0 else 0, b.y if b.y > 0 else 0)
... ret = func(a, b)
... if ret.x < 0 or ret.y < 0:
... ret = Coordinate(ret.x if ret.x > 0 else 0, ret.y if ret.y > 0 else 0)
... return ret
... return checker
>>> add = wrapper(add)
>>> sub = wrapper(sub)
>>> sub(one, two)
Coord: {'y': 0, 'x': 0}
>>> add(one, three)
Coord: {'y': 200, 'x': 100}
It's the same as doing the previous foo = outer (foo), but we can apply a useful checking mechanism to the processing results of the parameters and functions.
Python provides syntactic sugar with the @ symbol for describing decorators.
>>> add = wrapper(add)
Is the description
>>> @wrapper
... def add(a, b):
... return Coordinate(a.x + b.x, a.y + b.y)
It can be described in the form of.
How about it? It's a fairly high level story to read this far and create useful decorators like classmethod and staticmethod yourself, but at least it's not that difficult to use decorators-just prefix them with @decoratorname
. I hope you can think of it!
The decorator wrapper I wrote above is useful, but it can only be applied to a limited number of functions with two parameters. This time it's fine, but what if you want to write a decorator that can be applied to more functions? Is not it? Python also has features to support this. For more information, read the official documentation (http://docs.python.jp/2/tutorial/controlflow.html#tut-arbitraryargs). However, you can accept any number of required parameters by adding * (asterisk) to the parameters when defining the function.
>>> def one(*args):
... print args
>>> one()
()
>>> one(1, 2, 3)
(1, 2, 3)
>>> def two(x, y, *args):
... print x, y, args
>>> two('a', 'b', 'c')
a b ('c',)
You can see that any parameter part is passed as a list. Also, if you add * to the argument at the time of calling instead of defining it, the argument that has already become a list or tuple is unpacked and applied to the fixed argument. Will do it.
>>> def add(x, y):
... return x + y
>>> lst = [1,2]
>>> add(lst[0], lst[1]) #1
3
>>> add(*lst) #2 <- #Refers to exactly the same meaning as 1
3
There is also a notation ** (two asterisks). This corresponds to the dictionary format instead of the list.
>>> def foo(**kwargs):
... print kwargs
>>> foo()
{}
>>> foo(x=1, y=2)
{'y': 2, 'x': 1}
** Using kwargs in a function definition means that "all parameters not explicitly specified are stored in a dictionary named kwargs". * Like args, it also supports unpacking when calling a function. doing.
>>> dct = {'x': 1, 'y': 2}
>>> def bar(x, y):
... return x + y
>>> bar(**dct)
3
Let's write a decorator that outputs the function arguments to the log using the above function. For the sake of simplicity, the log output is printed to stdout.
>>> def logger(func):
... def inner(*args, **kwargs): #1
... print "Arguments were: %s, %s" % (args, kwargs)
... return func(*args, **kwargs) #2
... return inner
Notice that \ # 1 allows the inner function to take any number and format parameters, and \ # 2 allows it to be unpacked and passed as an argument. You can also apply the decorator logger to.
>>> @logger
... def foo1(x, y=1):
... return x * y
>>> @logger
... def foo2():
... return 2
>>> foo1(5, 4)
Arguments were: (5, 4), {}
20
>>> foo1(1)
Arguments were: (1,), {}
1
>>> foo2()
Arguments were: (), {}
2
If you understand the last example, you know about decorators! If you don't, go back to the previous step and read it over and over again. If you want to learn more, read the wonderful essay below by Bruce Eckel.
--
(*) callable is a general term for callable objects that take arguments and return results. Not only function but also class, method, generator, etc. are callable.
Recommended Posts