Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

11 knowledge points sharing of Python decorator

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the relevant knowledge of "11 knowledge points sharing of Python decorator". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

01. Hello, decorator

The use of the decorator is very fixed.

Define a decorator (hat) first

Then define your business function or class (person)

Finally, the decorator (hat) is fastened to the function (person).

Just like down there.

Def decorator (func): def wrapper (* args, * * kw): return func () return wrapper@decoratordef function (): print ("hello, decorator")

In fact, the decorator is not necessary for coding, which means that you can do without the decorator, and its appearance should make our code

More elegant, clearer code structure

Encapsulate the code that implements specific functions into decorators to improve the code reuse rate and enhance the readability of the code

Next, I'll show you how to write a variety of simple and complex decorators with examples.

02. Getting started: log printer

The first is the log printer.

Functions implemented:

Before the function is executed, print a line of log to tell the host that I am going to execute the function.

After the execution of the function, you can't pat your ass and leave. I can be polite code, and then print a log to tell the owner that I'm done.

# this is the decorator function, and the argument func is the decorated function def logger (func): def wrapper (* args, * * kw): print ('master, I'm ready to start executing: {} function:' .format (func.__name__)) # what is really executed is this line. Func (* args, * * kw) print ('master, I'm done.') Return wrapper

Suppose, my business function is to calculate the sum of two numbers. After you write it, put a hat on it directly.

@ loggerdef add (x, y): print ('{} + {} = {} '.format (x, y, x, y))

Then execute the add function.

Add (200,50)

Let's see what the output is.

Master, I am ready to start execution: add function: 200 + 50 = 250 master, I am finished. 03. Getting started: time timer

Let's take a look at the time timer.

Implementation function: as the name implies, it is to calculate the execution time of a function.

# this is the decorative function def timer (func): def wrapper (* args, * * kw): t1=time.time () # this is where the function really executes func (* args, * * kw) t2=time.time () # calculate the duration cost_time = t2-t1 print ("time: {} seconds" .format (cost_time)) return wrapper

Suppose, our function is to sleep for 10 seconds. In this way, we can better see whether the length of the calculation is reliable.

Import time@timerdef want_sleep (sleep_time): time.sleep (sleep_time) want_sleep (10)

Let's take a look at the output, as expected, for 10 seconds.

Time: 10.0073800086975098 seconds 04. Advanced: function decorator with parameters

Through the above two simple getting started examples, you should be able to understand how the decorator works.

However, the use of decorators is far more than that, further research, but also a lot of articles. Let's learn this knowledge point together today.

Looking back at the above example, the decorator cannot accept parameters. Its usage can only be applied to some simple scenarios. Decorators that do not pass parameters can only perform fixed logic on the decorated functions.

The decorator itself is a function, as a function, if you can not pass parameters, then the function of this function will be very limited, can only execute fixed logic. This means that if the execution of the decorator's logic code needs to be adjusted according to different scenarios, if we can't pass parameters, we have to write two decorators, which is obviously unreasonable.

For example, if we want to implement a task that can send emails regularly (send one per minute), and time synchronization tasks (once a day), we can implement a periodic_task (scheduled task) decorator by ourselves, which can receive parameters of a time interval and how often the task is executed.

Can be written like the following, because this function code is more complex, not conducive to learning, it will not be posted here.

@ periodic_task (spacing=60) def send_mail (): pass@periodic_task (spacing=86400) def ntp () pass

Let's create a pseudo scene by ourselves. We can pass a parameter in the decorator to indicate the nationality and say hello in our native language before the function is executed.

# Xiaoming, Chinese @ say_hello ("china") def xiaoming (): pass# jack, American @ say_hello ("america") def jack (): pass

What if we implement this decorator so that it can pass parameters?

It will be more complex and requires two layers of nesting.

Def say_hello (contry): def wrapper (func): def deco (* args, * * kwargs): if contry = = "china": print ("Hello!") Elif contry = "america": print ('hello.') Else: return # where the function is actually executed func (* args, * * kwargs) return deco return wrapper

Let's execute it.

Xiaoming () print ("-") jack ()

Look at the output.

Hello!-hello.05. High-order: class decorator without parameters

The above are all function-based decorators, and when reading other people's code, you can often find that there are class-based decorators.

Based on the implementation of the class decorator, two built-in functions _ _ call__ and _ _ init__ must be implemented.

_ _ init__: receive decorated function

_ _ call__: implements the decoration logic.

Or take the simple example of log printing as an example.

Class logger (object): def _ init__ (self, func): self.func = func def _ call__ (self, * args, * * kwargs): print ("[INFO]: the function {func} () is running..."\ .format (func=self.func.__name__)) return self.func (* args, * * kwargs) @ loggerdef say (something): print ("say {}!" .format (something)) say ("hello")

Execute it and look at the output

[INFO]: the function say () is running...say hellograph 06. High-order: class decorator with parameters

The above example without parameters, you found no, can only print INFO-level logs, normally, we also need to print DEBUG WARNING-level logs. This requires passing parameters to the class decorator and assigning a level to the function.

Class decorators with and without parameters are very different.

_ _ init__: no longer receives decorated functions, but receives incoming parameters.

_ _ call__: receives the decorated function to implement the decoration logic.

Class logger (object): def _ init__ (self, level='INFO'): self.level = level def _ call__ (self, func): # accept function def wrapper (* args, * * kwargs): print ("[{level}]: the function {func} () is running..."\ .format (level=self.level, func=func.__name__)) func (* args) * * kwargs) return wrapper # returns the function @ logger (level='WARNING') def say (something): print ("say {}!" .format (something)) say ("hello")

Let's specify the WARNING level, run it, and look at the output.

[WARNING]: the function say () is running...say hellograph 07. Using partial functions and classes to implement decorators

Most decorators are based on functions and closures, but this is not the only way to make decorators.

In fact, Python has only one requirement as to whether an object can be used in the form of a decorator (@ decorator): decorator must be an object that can be called (callable).

For this callable object, we are most familiar with functions.

In addition to functions, a class can also be a callable object, as long as the _ _ call__ function is implemented (I've already touched on the above examples).

There are also partial functions that are easily overlooked, which are actually callable objects.

Next, let's talk about how to use a combination of classes and partial functions to achieve a distinctive decorator.

As shown below, DelayFunc is a class that implements _ _ call__, and delay returns a partial function, where delay can be used as a decorator. (the following code is from the Python craftsman: tips for using decorators.)

Import timeimport functoolsclass DelayFunc: def _ init__ (self, duration, func): self.duration = duration self.func = func def _ _ call__ (self, * args, * * kwargs): print (f'Wait for {self.duration} seconds...') Time.sleep (self.duration) return self.func (* args, * * kwargs) def eager_call (self, * args, * * kwargs): print ('Call without delay') return self.func (* args, * * kwargs) def delay (duration): decorator: delay the execution of a function. It also provides the .eager _ call method to execute immediately "" # here to avoid defining additional functions, # use functools.partial directly to help construct the DelayFunc instance return functools.partial (DelayFunc, duration)

Our business function is very simple, which is to add

@ delay (duration=2) def add (a, b): return astatb

Let's take a look at the implementation process.

> add # it can be seen that add becomes the instance of Delay > add (3 call__Wait for 5) # directly call the instance and go to _ _ call__Wait for 2 seconds...8 > add.func # to implement the instance method 08. How to write decorators that can be decorated?

When writing singleton mode with Python, there are three common ways to write it. One of them is implemented with a decorator.

The following is the singleton writing of my own decorator version.

Instances = {} def singleton (cls): def get_instance (* args, * * kw): cls_name = cls.__name__ print ('= 1 =') if not cls_name in instances: print ('= 2 =') instance = cls (* args, * * kw) instances [CLS _ name] = instance return instances [CLS _ name] return get_instance@singletonclass User: _ instance = None def _ init__ (self, name): print ('= 3 =') self.name = name

You can see that we decorate the User class with the decorating function singleton. It is not common for a decorator to be used on a class, but as long as you are familiar with the implementation process of the decorator, it is not difficult to decorate the class. In the above example, the decorator simply implements control over the generation of class instances.

In fact, the process of instantiation, you can refer to my debugging process here to understand.

09. What is the use of wraps decorator?

There is a wraps decorator in the functools standard library, which you should see often, so what's the use of it?

Let's take a look at an example.

Def wrapper (func): def inner_function (): pass return inner_function@wrapperdef wrapped (): passprint (wrapped.__name__) # inner_function

Why is this happening? Shouldn't I return func?

This is not difficult to understand, because the upper execution func and the lower decorator (func) are equivalent, so the above func.__name__ is equivalent to the following decorator (func). _ _ name__, of course, the name is inner_function.

Def wrapper (func): def inner_function (): pass return inner_functiondef wrapped (): passprint (wrapper (wrapped). _ _ name__) # inner_function

So how to avoid this kind of situation? The method is to use the functools .modips decorator, which is used to assign some attribute values of the modified function (wrapped) to the modifier function (wrapper), and finally make the display of the attributes more intuitive.

From functools import wrapsdef wrapper (func): @ wraps (func) def inner_function (): pass return inner_function@wrapperdef wrapped (): passprint (wrapped.__name__) # wrapped

To be exact, wraps is actually a partial function object (partial). The source code is as follows

Def wraps (wrapped, assigned= WRAPPER_ASSIGNMENTS, updated= WRAPPER_UPDATES): return partial (update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)

You can see that wraps actually calls a function update_wrapper. After knowing the principle, we rewrite the above code and let wrapped.__name__ print out wrapped without using wraps. The code is as follows:

From functools import update_wrapperWRAPPER_ASSIGNMENTS = ('_ module__','_ name__','_ qualname__','_ doc__','_ annotations__') def wrapper (func): def inner_function (): pass update_wrapper (inner_function, func, assigned=WRAPPER_ASSIGNMENTS) return inner_function@wrapperdef wrapped (): passprint (wrapped.__name__) 10. Built-in decorator: property

All of the above are custom decorators.

In fact, the Python language itself has some decorators. For example, property, a built-in decorator, is all too familiar to us.

It usually exists in a class, and you can define a function as an attribute, and the value of the attribute is the content of the function return.

Usually we bind properties to an instance like this

Class Student (object): def _ init__ (self, name, age=None): self.name = name self.age = age# instantiation xiaoming = Student ("Xiaoming") # add attribute xiaoming.age=25# query attribute xiaoming.age# delete attribute del xiaoming.age

However, a little experienced developers can see that exposing the attribute directly, although it is easy to write, does not impose legal restrictions on the value of the attribute. In order to achieve this function, we can write like this.

Class Student (object): def _ init__ (self, name): self.name = name self.name = None def set_age (self, age): if not isinstance (age, int): raise ValueError ('illegal input: age must be numeric!') If not 0

< age < 100: raise ValueError('输入不合法:年龄范围必须0-100') self._age=age def get_age(self): return self._age def del_age(self): self._age = Nonexiaoming = Student("小明")# 添加属性xiaoming.set_age(25)# 查询属性xiaoming.get_age()# 删除属性xiaoming.del_age() 上面的代码设计虽然可以变量的定义,但是可以发现不管是获取还是赋值(通过函数)都和我们平时见到的不一样。 按照我们思维习惯应该是这样的。 # 赋值xiaoming.age = 25# 获取xiaoming.age 那么这样的方式我们如何实现呢。请看下面的代码。 class Student(object): def __init__(self, name): self.name = name self.name = None @property def age(self): return self._age @age.setter def age(self, value): if not isinstance(value, int): raise ValueError('输入不合法:年龄必须为数值!') if not 0 < value < 100: raise ValueError('输入不合法:年龄范围必须0-100') self._age=value @age.deleter def age(self): del self._agexiaoming = Student("小明")# 设置属性xiaoming.age = 25# 查询属性xiaoming.age# 删除属性del xiaoming.age 用@property装饰过的函数,会将一个函数定义成一个属性,属性的值就是该函数return的内容。同时,会将这个函数变成另外一个装饰器。就像后面我们使用的@age.setter和@age.deleter。 @age.setter 使得我们可以使用XiaoMing.age = 25这样的方式直接赋值。 @age.deleter 使得我们可以使用del XiaoMing.age这样的方式来删除属性。 property 的底层实现机制是「描述符」,为此我还写过一篇文章。 这里也介绍一下吧,正好将这些看似零散的文章全部串起来。 如下,我写了一个类,里面使用了 property 将 math 变成了类实例的属性 class Student: def __init__(self, name): self.name = name @property def math(self): return self._math @math.setter def math(self, value): if 0 >

> s1.math = 90in _ set__ > s1.mathin _ get__90

If you have questions about how the above code works, please be sure to understand it with the above two instructions, which are very important.

11. Other decorators: decorators in practice

After reading and understanding the above, you can be said to be a master of Python. Don't doubt it, be confident, because many people don't know there are so many uses for decorators.

In my opinion, the use of decorators can achieve the following goals:

Make the code more readable and forced.

The code structure is clearer and the code is less redundant.

It just so happens that I also have a scene recently, which can be well implemented with a decorator. Let's take a look at it for the time being.

This is a decorator that implements the timeout of the control function. If a timeout occurs, a timeout exception is thrown.

If you are interested, you can have a look.

Import signalclass TimeoutException (Exception): def _ init__ (self, error='Timeout waiting for response from Cloud'): Exception.__init__ (self, error) def timeout_limit (timeout_time): def wraps (func): def handler (signum, frame): raise TimeoutException () def deco (* args, * * kwargs): signal.signal (signal.SIGALRM, handler) signal.alarm (timeout_time) func (* args * * kwargs) signal.alarm (0) return deco return wraps "sharing of 11 knowledge points of Python decorator" ends here Thank you for your reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report