【问题标题】:Python: Is it 'proper' to replace global variables with class variablesPython:用类变量替换全局变量是否“正确”
【发布时间】:2014-12-08 17:41:00
【问题描述】:

请善待我,我是 Python 初学者 :-)

现在,我发现编写 Python 程序的“最佳实践”是将主代码包装在“主”函数中,并执行 if "__main__" == __name__: 测试以调用“主”函数。

这当然需要在“main”函数中使用一系列global 语句来访问全局变量。

我想知道将全局变量收集到自定义类中是否更合适(或者“Pythonic”,如果你愿意的话),比如_v,并使用_v. 前缀来引用变量?

另外,作为一个必然的问题,这会对性能或异常处理产生负面影响吗?


编辑:以下是程序的一般结构:

paramset = {
    0: { ...dict of params... }
    1: { ...dict of params... }
    2: { ...dict of params... }
    }

selector = 0
reset_requested = False
selector_change = False

def sighup_handler(signal,frame):
    global reset_requested
    logger.info('Caught SIGHUP, resetting to set #{0}'.format(new_selector))
    reset_requested = True
    selector = 0

def sigusr1_handler(signal,frame):
    global selector
    new_selector = (selector + 1) % len(paramset)
    logger.info('Caught SIGHUP, changing parameters to set #{0}'.format(new_selector))
    selector = new_selector
    selector_change = True

signal.signal(signal.SIGHUP, sighup_handler)
signal.signal(signal.SIGUSR1, sigusr1_handler)

def main():
    global reset_requested
    global selector
    global selector_change
    keep_running = True
    while keep_running
        logger.info('Processing selector {0}'.format(selector))
        for stage in [process_stage1, process_stage2, process_stage3]
            err, result = stage(paramset[selector])
            if err is not None:
                logger.critical('Stage failure! Err {0} details: {0}'.format(err, result))
                raise SystemError('Err {0} details: {0}'.format(err, result))
            else:
                logger.info('Stage success: {0}'.format(result))
            if reset_requested:
                stage_cleanup()
                reset_requested = False
            else:
                inter_stage_pause()
                if selector_change:
                    selector_change = False
                    break
        selector = (selector + 1) % len(paramset)

【问题讨论】:

  • @John1024 我已经编辑了我的问题,只添加了相关的逻辑。
  • 谢谢。这不是minimal working example,但看看是什么激发了这个问题非常有帮助。

标签: python global-variables class-variables


【解决方案1】:

一般来说,最好的做法是避免使用全局变量,而是通过方法调用将变量传递给需要它们的类/方法。示例:如果您正在制作计算器,请创建一个加法方法,该方法需要 2 个整数并返回一个整数。这与将 2 个输入 int 和 1 个输出 int 作为全局变量并让 add 方法对它们起作用形成对比。

【讨论】:

  • 好吧,在我的情况下,“用例”是我必须拦截几个信号(HUP、USR1 和 USR2),这将激活一些标志,这些标志将在适当的时候进行处理' 通过 main() 函数(即,向程序发送这些信号不会立即产生结果)。
  • 在python中,使用signal模块拦截HUP、USR1等信号。文档here。不需要全局变量。
  • 我知道。信号处理程序仅设置一个标志。主函数内的主循环将定期 - 在开始一个长进程之前 - 检查标志并相应地更改进程的各种参数。
  • @pepoluan 你能告诉我们你拥有的代码(或它的最小工作子集)吗?如果我们看到您的尝试,这将使我们更容易提供有用的反馈。谢谢。
  • 我从办公室回来后会编辑我的问题。
【解决方案2】:

示例代码中缺少足够多的部分,因此很难得出任何确定的结论。

事件驱动方法

此类问题的通常方法是使其完全由事件驱动。就目前而言,代码主要是轮询。例如,sighup_handler 设置 reset_requested = Truewhile 循环在 main 处理该请求。事件驱动的方法将处理重置,即直接调用stage_cleanup

def sighup_handler(signal,frame):
    logger.info('Caught SIGHUP, resetting to set #{0}'.format(new_selector))
    stage_cleanup()


具有共享变量的类

在示例代码中,所有这些 process_stages 和循环通过阶段的目的都不清楚。可以将它们全部放在事件驱动的上下文中吗?我不知道。如果它不能并且它确实需要共享变量,那么您对类的建议将是一个自然的选择。此类课程的开头可能如下所示:

class Main(object);

    def __init__(self):
        self.selector = 0
        self.selector_change = False
        signal.signal(signal.SIGHUP, self.sighup_handler)
        signal.signal(signal.SIGUSR1, self.sigusr1_handler)

    def sighup_handler(self, signal,frame):
        logger.info('Caught SIGHUP, resetting to set #{0}'.format(new_selector))
        stage_cleanup() 
        self.selector = 0

    def sigusr1_handler(self, signal,frame):
        new_selector = (selector + 1) % len(paramset)
        logger.info('Caught SIGHUP, changing parameters to set #{0}'.format(new_selector))
        self.selector = new_selector
        self.selector_change = True

    def mainloop(self):
        # Do here whatever polling is actually required.

if __name__ == '__main__':
    main = Main()
    main.mainloop()

再次,因为我不清楚轮询循环的真正目的,所以我没有尝试在上面的类中重现它的功能。

【讨论】:

  • 感谢您的解释!遗憾的是,事件驱动的信号处理方法并不适用,因为当请求复位时,如果某个阶段仍在运行,则该阶段必须运行直到完成。所以这就是为什么我只是在信号处理程序中设置一个标志,并让主循环处理阶段之间的标志。
  • @pepoluan 事件驱动的信号处理程序旨在处理此类问题。如果在“阶段”运行时不能处理复位,则解决方案是该阶段在其关键部分中"block" HUP 信号,然后稍后解除阻塞。甚至还有一种方法可以查看signals are pending while blocked 是什么。信号处理是一门成熟的艺术。
  • 嗯......这对我来说是新的。感谢您的链接!
  • 我刚刚注意到这些链接需要 py3.3。如果您使用的是旧版本的 python,this link 可能会有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-24
  • 2021-03-13
  • 2012-03-11
  • 2011-11-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多