Learn more about the use of decorators in Python
Decorator vs Decorator Pattern
First of all, everyone needs to understand that there may be a lot of worries about using the word decorator, because it can easily be confused with the decorator pattern in the book Design Patterns. Some other terminology was considered for this new feature, but decorators won out.
It is true that you can use python decorators to implement the decorator pattern, but this is definitely a very small part of its functionality and is a bit of a waste. For python decorators, I think it is the closest thing to macros.
History of macro
Macros have a very long history, but most people may have experience using C language preprocessing macros. However, there are some problems with macros in C language. (1) Macros do not exist in C language, (2) and the behavior of macros can sometimes be a bit weird, and often differ from the behavior of C language. Too consistent.
In order to support operations on some elements of the language itself, both Java and C# have added annotations. Of course, they all have some problems: sometimes in order to achieve your goals, you have to bypass a lot of pitfalls. Not finished yet, these annotation features will also be constrained by some inherent features of these languages (just like "Directing" described by Martin Fowler)
The slightly different thing is that many C++ programmers, including me, have realized the power of C++ templates and are already using this feature like macros.
Many other languages also include macro functions. Although I don’t know much about them, I am still willing to say without shame that Python decorators are very similar to Lisp macros in terms of power and richness of functions.
Macro target
I think it’s not too much to describe macros like this: Macros exist in a programming language to provide the ability to manipulate the language elements themselves. This is exactly what python decorators can do. They can modify functions and decorate this class. Compared to complex metaclasses, this may be the reason why people often provide a simple decorator.
The self-modification (metaprogramming) solutions provided by most programming languages have a major shortcoming, that is, there are too many restrictions and constraints, and it gives the illusion of writing other languages.
Python conforms to what Martin Fowler calls "Enabling" programming languages. So, if you want to perform modification operations (metaprogramming), why do you have to come up with a "different" or "restrictive" language? Why not just pick up python and start working on it yourself? This is what python decorators can do.
What can you do with Python decorators
Decorators allow you to "inject" or "modify" the code (logic) inside a function or class. In addition to being simpler and more powerful, decorators sound a bit like AOP aspect-oriented programming, right? For example, add something you want to do before the start or end of the method (such as some common operations in aspect-oriented programming such as permission checking, tracking, resource locking, etc.). With decorators, you can do this:
1 2 3 4 5 6 7 |
|
Function decorator
Functional decorators are usually placed before a function definition code to apply qualified decorators, such as:
1 2 3 |
|
When the compiler reaches this code, the function aFunction will be compiled, and the compiled function object will be passed to myDecorator. The decorator will generate a new function object to replace the original function aFunction.
So, what is the code implementation of the decorator myDecorator? Although most introductory examples of decorators write a function, I find that class-style decorators are easier to understand and more powerful than functional decorators.
The only thing that needs to be ensured is that the object returned by the decorator can be called like a function, so the class decorator needs to implement __call__.
What should the decorator do? Well, it can do anything, but usually you'd expect the original passed function to be executed somewhere, although this isn't mandatory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
When you execute this code, you will see output like this:
1 2 3 4 |
|
请注意,myDecorator的构造器实际是在装饰函数的时候执行的。我们可以在__init__()里面调用函数f,能够看到,在装饰器被调用之前,函数调用f()就已经完成了。另外,装饰器的构造器能够接收被装饰的方法。一般来讲,我们会捕捉到这个函数对象然后接下来在函数__call__()里面调用。装饰和调用是两个非常清晰明了的不同的步骤,这也是我为什么说类似装饰器更简单同时也更强大的原因。
当函数aFunction被装饰完成然后调用的时候,我们得到了一个完全不同的行为,实际上执行的是myDecorator.__call__()的代码逻辑,这是因为”装饰“把原有的代码逻辑用新的返回的逻辑给替换掉了。在我们的例子中,myDecorator对象替换掉了函数aFunction。事实上,在装饰器操作符@被加入之前,你不得不做一些比较low的操作来完成同样的事情:
1 2 |
|
因为有了@这个装饰器操作符, 你可以非常优雅的得到同样的结果:
1 2 |
|
不过也有不少人因为这一点反对装饰器,不过@仅仅是一个很小的语法糖而已,把一个函数对象传递给另外一个函数,然后用返回值替换原有的方法。
我觉着,之所以装饰器会产生这么大的影响是因为这个小小的语法糖完全改变了人们思考编程的方式。的确,通过将它实现成一个编程语言结构,它将”代码应用到代码上面“的思想带到了主流编程思维层面。
青出于蓝
现在我们实现一下第一个例子。在这里我们将会做一些很常规的事情,并且会使用这些代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
运行结果是:
1 2 3 4 5 6 |
|
现在我们能够看到,那些被装饰的方法有了“进入”和“离开”的跟踪信息。
构造器存储了通过参数传递进来的函数对象,在调用的方法里,我们用函数对象的__name__属性来展示被调用函数的名称,然后调用被装饰的函数自己。
使用函数作为装饰器
对于装饰器返回结果的约束只有一个,那就是能够被调用,从而它能够合理的替换掉原有的被装饰的那个函数。在上面的这些例子中,我们是将原有的函数用包含有__call__()的对象替换的。一个函数对象同样能够被调用,所以我们可以用函数来重写前一个装饰器的例子,像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
函数new_f()嵌套定义在entryExit的方法体里面,当entryExit被调用的时候,new_f()也会顺理成章地被返回。值得注意的是new_f()是一个闭包,捕获了参数变量f的值。
当new_f()定义完成后,它将会被entryExit返回,然后装饰器机制发生作用将结果赋值成被装饰的新方法。
代码print func1.__name__的输出结果是new_f,因为在装饰发生的过程中,原来的方法已经被替换成了new_f,如果对你来说这是一个问题的话,你可以在装饰器返回结果之前修改掉函数的名字:
1 2 3 4 5 6 7 |
|
你可以动态的获取函数的信息包括那些你做的更改,这在python里面非常有用。
带参数的装饰器
现在我们把上面的那个例子简单的改动一下,看看在添加装饰器参数的情况下会发生什么情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
从输出结果来看,运行的效果发生了明显的变化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
现在,在“装饰”阶段,构造器和__call__()都会被依次调用,__call__()也只接受一个函数对象类型的参数,而且必须返回一个装饰方法去替换原有的方法,__call__()只会在“装饰”阶段被调用一次,接着返回的装饰方法会被实际用在调用过程中。
尽管这个行为很合理,构造器现在被用来捕捉装饰器的参数,而且__call__()不能再被当做装饰方法,相反要利用它来完成装饰的过程。尽管如此,第一次见到这种与不带参数的装饰器迥然不同的行为还是会让人大吃一惊,而且它们的编程范式也有很大的不同。
带参数的函数式装饰器
最后,让我们看一下更复杂的函数式装饰器,在这里你不得不一次完成所有的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
输出结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
The return value of a functional decorator must be a function that can wrap the original wrapped function. In other words, Python will get and call the returned function result when decoration occurs, and then pass it to the decorated function. This is why we have three layers of nested functions defined in the implementation of the decorator. That function of the layer is the new replacement function.
Because of the nature of closures, wrapped_f() can access these parameters without explicitly storing the values arg1, arg2, and arg3 like in the class decorator example. However, this happens to be an example where I feel "explicit is better than implicit". Although functional decorators may be a bit more streamlined, class-style decorators are easier to understand and therefore easier to modify and maintain.

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

PHP is mainly procedural programming, but also supports object-oriented programming (OOP); Python supports a variety of paradigms, including OOP, functional and procedural programming. PHP is suitable for web development, and Python is suitable for a variety of applications such as data analysis and machine learning.

PHP is suitable for web development and rapid prototyping, and Python is suitable for data science and machine learning. 1.PHP is used for dynamic web development, with simple syntax and suitable for rapid development. 2. Python has concise syntax, is suitable for multiple fields, and has a strong library ecosystem.

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

VS Code can be used to write Python and provides many features that make it an ideal tool for developing Python applications. It allows users to: install Python extensions to get functions such as code completion, syntax highlighting, and debugging. Use the debugger to track code step by step, find and fix errors. Integrate Git for version control. Use code formatting tools to maintain code consistency. Use the Linting tool to spot potential problems ahead of time.

VS Code can run on Windows 8, but the experience may not be great. First make sure the system has been updated to the latest patch, then download the VS Code installation package that matches the system architecture and install it as prompted. After installation, be aware that some extensions may be incompatible with Windows 8 and need to look for alternative extensions or use newer Windows systems in a virtual machine. Install the necessary extensions to check whether they work properly. Although VS Code is feasible on Windows 8, it is recommended to upgrade to a newer Windows system for a better development experience and security.

PHP originated in 1994 and was developed by RasmusLerdorf. It was originally used to track website visitors and gradually evolved into a server-side scripting language and was widely used in web development. Python was developed by Guidovan Rossum in the late 1980s and was first released in 1991. It emphasizes code readability and simplicity, and is suitable for scientific computing, data analysis and other fields.

In VS Code, you can run the program in the terminal through the following steps: Prepare the code and open the integrated terminal to ensure that the code directory is consistent with the terminal working directory. Select the run command according to the programming language (such as Python's python your_file_name.py) to check whether it runs successfully and resolve errors. Use the debugger to improve debugging efficiency.

VS Code extensions pose malicious risks, such as hiding malicious code, exploiting vulnerabilities, and masturbating as legitimate extensions. Methods to identify malicious extensions include: checking publishers, reading comments, checking code, and installing with caution. Security measures also include: security awareness, good habits, regular updates and antivirus software.
