Python:我应该避免在块内部对变量进行初始化吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python:我应该避免在块内部对变量进行初始化吗?相关的知识,希望对你有一定的参考价值。

Problem

我有这样的代码

if condition:
    a = f(x)
else:
    a = g(y)

在块内初始化a对我来说很糟糕。可以写得更好吗?

我不能使用三元运算符,因为函数和/或参数列表的名称很长。说“长”我的意思是以下表达式

a = f(x) if condition else g(y)

将使用真实姓名超过79(有时甚至超过119)符号而不是afgxycondition。使用多个斜杠会使代码变得丑陋和混乱。

我不想用默认的函数之一来初始化a,因为这两个函数都很慢,我不能允许这样的开销

a = g(y)
if condition:
    a = f(x)

我可以使用None初始化变量,但这个解决方案是否足够?

a = None
if condition:
    a = f(x)
else:
    a = g(y)

让我解释一下我的立场:在C和C ++中,块内部的变量都以块作为其范围。在ES6中引入了let关键字 - 它允许使用与C和C ++中的变量相同的范围规则创建变量。使用旧的var关键字定义的变量具有与Python类似的范围规则。这就是为什么我认为如果我想在这些块之外使用变量,那么变量的初始化应该在块外部进行。

更新

这是一个更复杂的例子

for obj in gen:
    # do something with the `obj`
    if predicate(obj):
        try:
            result = f(obj)
        except Exception as e:
            log(e)
            continue
    else:
        result = g(obj)
    # do something useful with the `result`
else:
    result = h(obj)

display(result)

我浏览了一些生成器gen的元素,处理它们并在每次迭代时对result执行一些操作。然后我想用循环外的最后一个result做点什么。

它是pythonic足以不事先为result分配一个虚拟值吗?这不会使代码的可读性降低吗?

Question

if / else / for / etc中初始化变量是否合适?在Python?

答案

好的,这里有两点需要澄清,这些是python的基础。

  1. python中没有变量声明/初始化。像a = f(x)这样的表达式只是一个将f返回的对象命名为a的方案。 namea可以在以后用于命名任何其他对象,无论其类型是什么。见this答案。
  2. python中的块是模块的主体,类或函数。在这些对象中定义/命名的任何内容在以后的代码中都可见,直到块结束。循环或if-else不是块。因此,在循环外部定义的任何名称或if / else将在内部可见,反之亦然。见thisglobalnonlocal对象有点不同。 python中没有let,因为这是默认行为。

在您的情况下,唯一的问题是如何在代码中进一步使用a。如果您的代码需要fg返回的对象类型,除非出现错误,否则它应该正常工作。因为if或else中的至少一个应该在正常操作中运行,所以a将引用某种对象(如果名称在if和else中是不同的那将是一个问题)。如果要确保后续代码不会中断,可以使用try-except-else捕获函数生成的任何错误,并在适当报告/记录错误后为except子句中的a分配默认值。

因此,总结并直接解决您的问题,为if-else语句或循环内的对象分配名称是非常好的做法:

  1. 在if和else子句中使用相同的名称,以便保证名称引用语句末尾的对象。额外的try-except-else错误捕获可以处理由函数引发的异常。
  2. 这些名称不应该太短,一般或不能使代码的意图明确像ares等。一个明智的名称将导致更好的可读性,并防止意外使用相同的名称以后为其他一些对象失去原来的。
另一答案

Python没有块范围......范围是整个函数,它是完美的pythonic编写

if <condition>:
    a = f()
else:
    a = g()

如果你想用C ++编写,然后使用C ++编写C ++,不要使用Python编写C ++ ...这是一个坏主意。

另一答案

让我澄清一下我在评论中的含义。

#this is not, strictly, needed, but it makes the 
#exception handler more robust
a = b = None

try:
    if condition:
        a = f(x)
        b = v(x)
    else:
        a = g(y)
        b = v2(x)

    return w(a, b)

except Exception, e:
    logger.exception("exception:%s" % (e))
    logger.exception("  the value of a was:%s" % (a))
    logger.exception("  the value of b was:%s" % (b))
    raise 

这是非常标准的代码,你只想在异常的情况下将整个东西包装在一些日志代码中。我重新提升原始异常,但可以轻松返回默认值。

问题是,除非异常等到return w(a, b)发生,否则任何对ab的访问都会根据那些未声明的变量抛出自己的NameError。

这种情况发生在我身上很多,使用自定义web单元测试代码 - 我从一个获取或发布到网址得到一个响应,我对响应进行了一系列测试。如果原始的get / post失败,则响应不存在,因此任何诊断(例如,相当打印响应的属性)都会引发异常,从而迫使您在异常处理程序有用之前进行清理。

因此,为了防止这种情况,我将异常处理程序中引用的任何变量初始化为None。当然,如果需要,你还必须防范aNone,像logger("a.attr1:%s" % (getattr(a, "attr1","?")这样的东西

以上是关于Python:我应该避免在块内部对变量进行初始化吗?的主要内容,如果未能解决你的问题,请参考以下文章

为啥实例变量在块内时似乎消失了?

Python 2.7 - 这是对 __metaclass__ 的有效使用吗? [关闭]

我应该将变量声明为尽可能接近它们将被使用的范围吗?

请问一下python里的pycurl初始化,pycurl.Curl()必须能够联网才能初始化正确吗? 我本想在局域网内部测试。

我应该在 zeroMQ 程序初始化中添加睡眠以避免 heisenbugs 吗?

我应该总是为失败的初始化方法释放自我吗?