未加星标

Python Decorators Demystified

字体大小 | |
[开发(python) 所属分类 开发(python) | 发布者 店小二04 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

This articile was published earlier on Medium .

Decorators are one of the elegant features of the python programming language. They are heavily used in modern libraries and frameworks.

The decorators are very good tools to encapsulate lot of implementation details and leaving out very simple interface. Let’s look at the following example:

@login_required def edit_post(post_id): ...

The login_required is a decorator that makes sure that the user is logged in before they can edit a post. It’ll take care of redirecting to login page, setting the right query parameters to redirect back to same page after successful login etc. All that the developer of the function has to do is, put @login_required before the function.

While using decorators is very simple, writing decorators is something even experienced Python developers get confused lot of times. In this article I’m going to explain how Python decorators work in a few simple steps.

Functions are first-class objects

In Python, functions are first-class objects. That just means that functions are values just like numbers, strings and lists. Let’s look at the following example.

def square(x): return x*x print(square(4)) ## 16

The output of the example is shown in comments at its bottom.

The above def statement creates a function square and assigns it to a variable with the same name. So we’ll be able to use that variable like just any other variable. For example, we’ll be able to assign it to another variable.

f = square print(f(4)) ## 16

The following picture explains the same thing clearly.


Python Decorators Demystified
Functions can take other functions as arguments

Now that we know functions are nothing special, they can be passed as arguments to other functions. For example, the following function takes a function and two numbers as arguments.

def sumof(f, x, y): return f(x) + f(y) print(sumof(square, 3, 4)) print(sumof(len, “hello”, “python”)) ## 25 ## 11

And nothing stops us from returning a function from a function call.

def make_adder(x): def add(y): return x+y return add add3 = make_adder(3) print(add3(4)) ## 7

Since the function add is defined inside the make_adder function, it’ll have access to the variables of that function. So the code in the add function can access the variable x defined in the enclosed scope.

When make_adder is called with 3 as argument, it returns an add function with x set to 3. When we call make_adder again, it returns a different add function, with possibly different value of x .

Functions can take variable number of arguments

In Python, it is possible for a function to take variable number of arguments.

def strjoin(sep, *words): return sep.join(words) print(strjoin("-", "one", "two")) print(strjoin("-", "one", "two", "three")) ## one-two ## one-two-theee

The above strjoin function receives its first argument in the variable sep and all other arguments packed as a tuple in the variable words.

It is also possible to do the reverse of that. If we have a list (or tuple) of arguments that we want to pass to a function, we can unpack them when making the function call.

def add(x, y): return x+y args = [3, 4] print(add(*args)) ## 7

Here is another example:

def info(*args): print("[INFO]", *args) def warn(*args): print("[WARN]", *args) info("connection established") warn("hand shake failed. retrying...") ## [INFO] connection established ## [WARN] hand shake failed. retrying... Decorator is just a syntactic sugar

The @decorator syntax is a shorthand for something really simple. For example:

@decorator def func(): ...

is equivalent to:

def func(): … func = decorator(func)

The decorator takes a function as argument and returns a function back, possibly a new function.

Let’s write a simple decorator to understand it better.

def trace(f): def g(x): print(f.__name__, x) return f(x) return g

This trace function takes a function as argument and returns a new function that behaves like the original function, except it prints the function name and argument for every call.

@trace def square(x): return x*x @trace def double(x): return x+x print(square(4)) print(square(5)) print(double(4)) print(double(5)) ## square 4 ## 16 ## square 5 ## 25 ## double 4 ## 8 ## double 5 ## 25

There is one drawback of this decorator. This can work only with functions that take one argument. Usually, decorators are written to handle functions with any number of arguments. Lets improve our trace decorator to work with functions taking any number of arguments.

def trace(f): def g(*args): print(f.__name__, args) return f(*args) return g

And here is an example of using it:

@trace def square(x): return x*x @trace def sum_of_squares(x, y): return square(x) + square(y) print(sum_of_squares(3, 4)) ## sum_of_squares (3, 4) ## square (3,) ## square (4,) ## 25

With a little more work we can make it print very nicely.

level = 0 def trace(f): def g(*args): global level # pretty print indicating the level prefix = "| " * level + "|--" strargs = ", ".join(repr(a) for a in args) print("{} {}({})".format(prefix, f.__name__, strargs)) # increment the level before calling the function # and decrement it after the call level += 1 result = f(*args) level -= 1 return result return g

With this trace decorator, the previous sum_of_squares example would produce:

|-- sum_of_squares (3, 4) | |-- square (3,) | |-- square (4,) 25

It would be fun to try it with a recursive function. Let’s try it with the famous fibonacci function.

@trace def fib(n): if n == 0 or n == 1: return 1 else: return fib(n-1) + fib(n-2) print(fib(5))

See what it produces:

|-- fib(5) | |-- fib(4) | | |-- fib(3) | | | |-- fib(2) | | | | |-- fib(1) | | | | |-- fib(0) | | | |-- fib(1) | | |-- fib(2) | | | |-- fib(1) | | | |-- fib(0) | |-- fib(3) | | |-- fib(2) | | | |-- fib(1) | | | |-- fib(0) | | |-- fib(1) 8

Now, we have a beautiful decorator to trace function calls!

Hope this article made you understand how Python decorators work.

Thanks to Shreyas Satish reading draft of this and suggestions.

If you are excited about decorators and curious to learn more about them, check out my upcoming Python Decorators Demystified online workshop being held on Oct 22 23. It is going to be a hands-on workshop with lot of examples and practice problems covering some advanced use cases of decorators.

本文开发(python)相关术语:python基础教程 python多线程 web开发工程师 软件开发工程师 软件开发流程

主题: Python
分页:12
转载请注明
本文标题:Python Decorators Demystified
本站链接:http://www.codesec.net/view/485508.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(python) | 评论(0) | 阅读(26)