Python:我应该避免初始化块内的变量吗?



>问题

我有这样的代码

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

块内a的初始化对我来说看起来很糟糕。能写得更好吗?

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

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

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

我不想用 defaul 其中一个函数的结果初始化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++ 中的变量具有相同范围规则的变量。使用 oldvar关键字定义的变量具有与 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分配虚拟值? 这不会降低代码的可读性吗?

问题

在Python 中初始化if/else/for/etc. 中的变量好吗?

Python 没有块作用域...范围是整个函数,编写起来非常pythonic

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

如果你想用C++写,那就用C++写C++,不要用 Python 写C++......这是个坏主意。

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

  1. python中没有变量声明/初始化。像a = f(x)这样的表达式只是将 f 返回的对象命名为a的方案。该名称a以后可用于命名任何其他对象,无论其类型如何。看到这个答案。

  2. python中的块是模块,类或函数的主体。在这些对象中定义/命名的任何内容对后面的代码都是可见的,直到块的末尾。循环或 if-else 不是块。因此,在循环外部之前定义的任何名称或 if/else 将在循环内部可见,反之亦然。看到这里。globalnonlocal对象略有不同。python 中没有let,因为这是默认行为。

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

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

  1. if 和 else 子句中使用相同的名称,以便保证名称引用语句末尾的对象。其他try-except-else错误捕获可以处理函数引发的异常。

  2. 名称不应太短、太通用或不能明确代码意图的内容,如ares等。一个合理的名称将导致更好的可读性,并防止以后意外地将相同的名称用于其他对象,从而丢失原始名称。

让我澄清一下我在评论中的意思。

#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 单元测试代码 - 我从获取或发布到 url 获得响应,并针对响应运行一堆测试。 如果原始 get/post 失败,则响应不存在,因此任何诊断(例如漂亮打印该响应的属性)都会引发异常,迫使您在异常处理程序有用之前清理内容。

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

最新更新