程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Extending Python timers with context manager

編輯:Python

author | Cloud King

source |  data STUDIO

In this paper , We studied together Hand in hand to teach you to achieve a Python timer . In this paper , Yunduo Jun will understand what is Context manager   and Python Of  with sentence , And how to complete customization . Then extend  Timer  So that it can Used as context manager . Last , Use Timer How to simplify our own code as a context manager .

The first one we created above Python Timer class , Then gradually expand us Timer class , Its code is also relatively rich and powerful . We can't be satisfied with this , You still need some code from the template to use Timer

  • First , Instantiate the class

  • secondly , Call before the code block to be timed .start()

  • Last , Call after the code block .stop()

One Python Timer context manager

Python There is a unique structure , Used to call a function before and after a code block : Context manager .

understand Python Context manager in

Context managers have long been Python An important part of . from PEP 343 On 2005 In introducing , And for the first time in Python 2.5 To realize . have access to with Keyword identifies the context manager in the code :

with EXPRESSION as VARIABLE:
    BLOCK

EXPRESSION Are some that return to the context manager Python expression . First, the context manager is bound to the variable name  VARIABLE On ,BLOCK Can be any conventional Python Code block . The context manager guarantees that the program is in BLOCK Before calling some code , stay BLOCK Call some other code after execution . Even though BLOCK Trigger exception , The latter will also be implemented .

The most common use of a context manager is to handle different resources , Such as file 、 Locks and database connections . The context manager is used to release and clean up resources after they are used . The following example demonstrates this only by printing lines containing colons  timer.py Basic structure . Besides , It shows that in Python Common idioms for opening files in :

with open("timer.py") as fp:
    print("".join(ln for ln in fp if ":" in ln))
class TimerError(Exception):
class Timer:
    timers: ClassVar[Dict[str, float]] = {}
    name: Optional[str] = None
    text: str = "Elapsed time: {:0.4f} seconds"
    logger: Optional[Callable[[str], None]] = print
    _start_time: Optional[float] = field(default=None, init=False, repr=False)
    def __post_init__(self) -> None:
        if self.name is not None:
    def start(self) -> None:
        if self._start_time is not None:
    def stop(self) -> float:
        if self._start_time is None:
        if self.logger:
        if self.name:

Be careful , Use open() As context manager , The file pointer fp  Does not explicitly close , Can confirm fp Automatically closed :

fp.closed
True

In this example ,open("timer.py") Is an expression that returns the context manager . The context manager is bound to a name fp. Context manager in print() Valid during execution . This single line code block is in fp In the context of .

fp What does context manager mean ? Technically speaking , Namely fp Realized Context Manager Protocol .Python There are many different protocols at the bottom of the language . You can think of a protocol as a contract that states what specific methods our code must implement .

The context manager protocol consists of two methods :

  1. Called when the context associated with the context manager is entered .__enter__().

  2. Called when exiting the context associated with the context manager .__exit__().

let me put it another way , Create your own context manager , You need to write an implementation .__enter__() and .__exit__() Class . try Hello, World! The context manager example :

# studio.py
class Studio:
    def __init__(self, name):
        self.name = name
    def __enter__(self):
        print(f" Hello  {self.name}")
        return self
    def __exit__(self, exc_type, exc_value, exc_tb):
        print(f" See you later , {self.name}")

Studio It's a context manager , It implements the context manager protocol , Use as follows :

from studio import Studio
with Studio(" Cloud King "):
    print(" Busy  ...")
 Hello Cloud King
Busy ...
See you later , Cloud King 

First , Be careful .__enter__() How to be called before doing something , and .__exit__() Is called after doing something . In the example , There is no reference to the context manager , Therefore, it is not necessary to use as Name the context manager .

Next , Be careful  self.__enter__() The return value of is restricted by as constraint . When creating the context manager , Usually want from .__enter__() return self . The return value can be used as follows :

from greeter import Greeter
with Greeter(" Cloud King ") as grt:
  print(f"{grt.name}  Busy  ...")
 Hello Cloud King
Cloud King Busy ...
See you later , Cloud King 

Writing __exit__ Function time , Something to pay attention to , It must have these three parameters :

  • exc_type: Exception types

  • exc_val: outliers

  • exc_tb: Abnormal error stack information

These three parameters are used for error handling in the context manager , They use  sys.exc_info() The return value of returns . When the main logic code does not report an exception , All three parameters will be None.

If an exception occurs while executing the block , Then the code will use the exception type 、 Exception instances and backtracking objects ( namely exc_typeexc_value and exc_tb) call .__exit__() . Usually , These are ignored in the context manager , Call before throwing an exception .__exit__()

from greeter import Greeter
with Greeter(" Cloud King ") as grt:
    print(f"{grt.age} does not exist")
 Hello Cloud King
See you later , Cloud King
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'Greeter' object has no attribute 'age'

You can see , Even if there are errors in the code , Still printed " See you later , Cloud King ".

  Understand and use contextlib

Now we have a preliminary understanding of what a context manager is and how to create our own context manager . In the example above , We just want to build a context manager , But wrote a class . If you just want to implement a simple function , Writing a class is a little too complicated . Now , We just want to , If only one function can be written to implement the context manager .

This point Python I've thought about it for a long time . It provides us with a decorator , You just need to implement the function content according to its code Protocol , You can turn this function object into a context manager .

We are in accordance with the contextlib To implement a context manager , To be more intuitive, let's change the use case , Create an open file that we are familiar with (with open) Context manager for .

import contextlib
@contextlib.contextmanager
def open_func(file_name):
    # __enter__ Method
    print('open file:', file_name, 'in __enter__')
    file_handler = open(file_name, 'r')
 
    # 【 a key 】:yield
    yield file_handler
    # __exit__ Method
    print('close file:', file_name, 'in __exit__')
    file_handler.close()
    return
with open_func('test.txt') as file_in:
    for line in file_in:
        print(line)

In the decorated function , Must be a generator ( with yield), and yield Previous code , Equivalent to __enter__ Contents of Li .yield Later code , Equivalent to __exit__ Contents of Li .

The above code can only achieve the first purpose of the context manager ( Management resources ), Can not achieve the second purpose ( Handling exceptions ).

If you want to handle exceptions , You can change it to the following .

import contextlib
@contextlib.contextmanager
def open_func(file_name):
    # __enter__ Method
    print('open file:', file_name, 'in __enter__')
    file_handler = open(file_name, 'r')
    try:
        yield file_handler
    except Exception as exc:
        # deal with exception
        print('the exception was thrown')
    finally:
        print('close file:', file_name, 'in __exit__')
        file_handler.close()
        return
with open_func('test.txt') as file_in:
    for line in file_in:
        1/0
        print(line)

Python In the standard library contextlib Includes a convenient way to define a new context manager , And can be used to close objects 、 Out of the box context managers that suppress errors or even do nothing !

establish Python Timer context manager

After understanding the general working mode of the context manager , Want to know how they help with temporal code ? Suppose if you could Run some functions before and after the code block , Then it can be simplified Python How the timer works . Actually , The context manager can automatically call... Explicitly for timing .start() and .stop().

Again , Must let Timer Working as a context manager , It needs to comply with the context manager protocol , let me put it another way , It has to be implemented .__enter__() and .__exit__()  Method to start and stop Python timer . As you can see from the current code , All the necessary functions are already available , So just add the following methods to the Timer Class :

# timer.py
@dataclass
class Timer:
    #  Other code remains the same
    def __enter__(self):
        """Start a new timer as a context manager"""
        self.start()
        return self
    def __exit__(self, *exc_info):
        """Stop the context manager timer"""
        self.stop()

Timer Now it is a context manager . An important part of the implementation is when entering the context ,.__enter__() call .start() start-up Python timer , And when the code leaves the context ,.__exit__() Use .stop() stop it Python timer .

from timer import Timer
import time
with Timer():
    time.sleep(0.7)
Elapsed time: 0.7012 seconds

Notice two more subtle details here :

  • .__enter__() return self,Timer  example , It allows users to use as take  Timer  Instance bound to variable . for example , Use with Timer() as t: Will create points to Timer  Object's variables t.

  • .__exit__() Three parameters are required , It contains information about any exceptions that occur during context execution . In the code , These parameters are packaged in a file named exc_info In the tuple of , Then ignored , here Timer No exception handling will be attempted .

In this case, no exceptions are handled . A big feature of the context manager is , Exit regardless of the context , Will ensure that .__exit__(). In the following example , Create divide by zero formula to simulate exception view code function :

from timer import Timer
with Timer():
    for num in range(-3, 3):
        print(f"1 / {num} = {1 / num:.3f}")
1 / -3 = -0.333
1 / -2 = -0.500
1 / -1 = -1.000
Elapsed time: 0.0001 seconds
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
ZeroDivisionError: division by zero

Be careful , Even if the code throws an exception ,Timer  The elapsed time will also be printed .

Use Python Timer context manager

Now we will learn how to use  Timer  Context manager to time " Download data " Program . Think back to how you used Timer Of :

# download_data.py
import requests
from timer import Timer
def main():
    t = Timer()
    t.start()
    source_url = 'https://cloud.tsinghua.edu.cn/d/e1ccfff39ad541908bae/files/?p=%2Fall_six_datasets.zip&dl=1'
    headers = {'User-Agent': 'Mozilla/5.0'}
    res = requests.get(source_url, headers=headers) 
    t.stop()
    with open('dataset/datasets.zip', 'wb') as f:
        f.write(res.content)
if __name__ == "__main__":
    main()

We are right now requests.get() Call to time monitor . Using the context manager, you can make your code shorter 、 It's simpler 、 Easier to read

# download_data.py
import requests
from timer import Timer
def main():
    source_url = 'https://cloud.tsinghua.edu.cn/d/e1ccfff39ad541908bae/files/?p=%2Fall_six_datasets.zip&dl=1'
    headers = {'User-Agent': 'Mozilla/5.0'}
    with Timer():
        res = requests.get(source_url, headers=headers)
        
    with open('dataset/datasets.zip', 'wb') as f:
        f.write(res.content)
if __name__ == "__main__":
    main()

This code is actually the same as the code above . The main difference is that there are no independent variables defined t, There is nothing superfluous in the namespace .

At the end

Add context manager functionality to Python The timer class has several advantages :

  • Time saving and labor saving : Only one extra line of code is required to time the execution of the code block .

  • High readability : Calling the context manager is readable , You can more clearly visualize the code block you are timing .

Use Timer As a context manager, it is almost used directly .start() and .stop() Just as flexible , It also has less boilerplate code . In the next article in this series , Yunduo Jun will learn how to Timer Also used for Decorator , And used in code , Thus, it is easier to monitor the complete running process of the code , Let's look forward to !

Looking back

NLP Exploration and practice of class problem modeling scheme

Python Common encryption algorithms in crawlers !

2D Transformation 3D, Look at NVIDIA's AI“ new ” magic !

How to use Python Realize the security system of the scenic spot ?

 Share
Point collection
A little bit of praise
Click to see 

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved