python-3.x 创建全局变量的副本()

0sgqnhkj  于 2023-04-08  发布在  Python
关注(0)|答案(1)|浏览(120)

在python中,我试图编写一个实用函数来计算函数运行的时间,它基本上是一个帮助函数,可以使timeit更容易使用。
下面是我的代码。如果有任何看起来奇怪的地方,那是因为我试图尽可能多地修剪代码,使其尽可能简单:

import numpy as np
import timeit
import requests
from enum import Enum
import copy

class LoggingLevel(Enum):
    Debug = 1
    Info = 2
    Important = 3
    VeryImportant = 4
    SuperImportant = 5
    Warning = 6

class Logging:

    @staticmethod
    def log(message: str, level: LoggingLevel, special, colorized):
        colors = {
            "Debug": "\033[0m",
            "Info": "\033[94m",
            "Important": "\033[95m",
            "VeryImportant": "\033[96m",
            "SuperImportant": "\033[93m",
            "Warning": "\033[91m",
            "Special": "\033[92m",
            "reset": "\033[0m"
        }
        if colorized:
            if special:
                print(f"{colors['Special']}[{level.name}] [Special]: {message}{colors['reset']}")
            else:
                if level.name in colors:
                    print(f"{colors[level.name]}[{level.name}]: {message}{colors['reset']}")
                else:
                    print(f"[{level.name}]: {message}")

logging = Logging()

def check_internet(testurl: str = "https://www.example.com", timeout: float = 5) -> bool:
    logging.log("Checking for internet connection with timeout of"
                f" {timeout if timeout is not None else ''} seconds...",
                LoggingLevel.Info, special=False, colorized=True)
    logging.log(f"Using {testurl} to check for internet connection", LoggingLevel.Debug, special=False, colorized=True)
    try:
        requests.get(testurl, timeout=timeout)
        logging.log(f"Successfully confirmed internet connection!", LoggingLevel.Info, special=False, colorized=True)
        return True
    except (requests.ConnectionError, requests.Timeout):
        return False

def time(code: str, useglobals: bool = True, sets: int = 3, trialsperset: int = 10) -> float:
    """
    Times how long a segment of code takes to run (on average) using the timeit module
    Usually used to test functions

    :param code: The segment of code to test
    :param useglobals: Whether to use the current globals or no globals in the
    timing sandbox (the code may not work without them)
    :param sets: The number of sets
    :param trialsperset: The number of times to run the code each set
    (Total number of times run = sets * trials)
        the higher the number, the more accurate, but also the longer it will take
    :return: Average amount of time the code takes to run (seconds)

    """

    # noinspection IncorrectFormatting
    class NopClass:
        """ From https://stackoverflow.com/a/24946360/20558255 """

        def nop(*args, **kwargs): pass

        def __getattr__(self, _): return self.nop

    nopclass = NopClass()
    nopfunction: callable = lambda *args, **kwargs: None

    logging.log(f"Testing how long {code} takes to run in {sets} sets"
                f" of {trialsperset} trials...", LoggingLevel.Debug, special=False, colorized=True)
    if useglobals:
        sandboxglobals = copy.deepcopy(globals())
        # We nop logging class and print function so we don't get flooded with print messages
        sandboxglobals["print"] = nopfunction
        sandboxglobals["logging"] = nopclass
    else:
        sandboxglobals = None
    times = timeit.repeat(stmt=code, repeat=sets, number=trialsperset, globals=sandboxglobals)
    return np.mean(times)

time("check_internet()", useglobals=True, sets=3, trialsperset=10)

希望你能理解为什么我(尝试)将globals()复制到sandboxglobals,并对sandboxglobals进行一些更改,然后将sandboxglobals传递给timeit.repeat:我想禁用打印和日志记录的代码 * 当它被计时 *.然而,我不想禁用打印和日志记录的真实的的程序.虽然在这种情况下,将被禁用打印和日志记录的真正的程序调用timeit.repeat()之前,然后重新启用它们后,在其他情况下(异步或多线程代码,例如),这将是重要的.
然而,deepcopy不工作。当我运行代码时,我得到以下错误:

Traceback (most recent call last):
  File "Test.py", line 98, in <module>
    time("check_internet()", useglobals=True, sets=3, trialsperset=10)
  File "Test.py", line 87, in time
    sandboxglobals = copy.deepcopy(globals())
  File "C:\Users\zachy\AppData\Local\Programs\Python\Python310\lib\copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "C:\Users\zachy\AppData\Local\Programs\Python\Python310\lib\copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "C:\Users\zachy\AppData\Local\Programs\Python\Python310\lib\copy.py", line 161, in deepcopy
    rv = reductor(4)
TypeError: cannot pickle 'module' object

我明白这个错误是什么意思,但我不知道如何解决它。我尝试了所有我能找到的方法,但我无法找到一种方法来深度复制而不pickle。
浅拷贝不起作用,因为它不是递归的-它将禁用print,因为这是globals() dict中的顶级,但它不会禁用logging.log。作为证明,这里是将deepcopy更改为copy后的日志:

[Debug]: Testing how long check_internet() takes to run in 3 sets of 10 trials...
[Info]: Checking for internet connection with timeout of 5 seconds...
[Debug]: Using https://www.example.com to check for internet connection
[Info]: Successfully confirmed internet connection!
[Info]: Checking for internet connection with timeout of 5 seconds...
[Debug]: Using https://www.example.com to check for internet connection
[Info]: Successfully confirmed internet connection!
... [TRUNCATED]

有没有什么方法可以实现我想要的?或者是不可能的?我是否错过了什么?谢谢。

lc8prwob

lc8prwob1#

由于您不需要对sandboxglobals中的模块信息进行任何修改,因此您可以跳过从globals深度复制它们。

# noinspection IncorrectFormatting
    class NopClass:
        """ From https://stackoverflow.com/a/24946360/20558255 """

        def nop(*args, **kwargs): pass

        def __getattr__(self, _): return self.nop

    nopclass = NopClass()
    nopfunction: callable = lambda *args, **kwargs: None

    logging.log(f"Testing how long {code} takes to run in {sets} sets"
                f" of {trialsperset} trials...", LoggingLevel.Debug, special=False, colorized=True)
    if useglobals:
        sandboxglobals = {}
        for k,v in globals().items():
            try:
                # entries of modules raise exception
                # do not deepcopy them
                tmp_dict = copy.deepcopy({k:v})
                sandboxglobals.update(tmp_dict)
            except:
                tmp_dict = {k:v}
                sandboxglobals.update(tmp_dict)
        # We nop logging class and print function so we don't get flooded with print messages
        sandboxglobals["print"] = nopfunction
        sandboxglobals["logging"] = nopclass
    else:
        sandboxglobals = None
    times = timeit.repeat(stmt=code, repeat=sets, number=trialsperset, globals=sandboxglobals)
    return np.mean(times)

相关问题