>问题
我有这样的代码
if condition:
a = f(x)
else:
a = g(y)
块内a
的初始化对我来说看起来很糟糕。能写得更好吗?
我不能使用三元运算符,因为函数名称和/或参数列表很长。 说"长"我的意思是以下表达
a = f(x) if condition else g(y)
将采用超过 79 个(有时甚至超过 119 个)具有真实名称的符号,而不是a
、f
、g
、x
、y
和condition
。 使用多个斜杠会使代码变得丑陋和混乱。
我不想用 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的基础。
-
python中没有变量声明/初始化。像
a = f(x)
这样的表达式只是将 f 返回的对象命名为a
的方案。该名称a
以后可用于命名任何其他对象,无论其类型如何。看到这个答案。 -
python中的块是模块,类或函数的主体。在这些对象中定义/命名的任何内容对后面的代码都是可见的,直到块的末尾。循环或 if-else 不是块。因此,在循环外部之前定义的任何名称或 if/else 将在循环内部可见,反之亦然。看到这里。
global
和nonlocal
对象略有不同。python 中没有let
,因为这是默认行为。
在您的情况下,唯一关心的是如何在代码中进一步使用a
。如果代码需要f
或g
返回的对象类型,则除非出现错误,否则它应该可以正常工作。因为至少有一个 if 或 else 应该在正常操作中运行,因此a
将引用某种对象(如果名称在 if 中不同,否则这将是一个问题)。如果要确保后续代码不会中断,可以使用try-except-else
来捕获函数生成的任何错误,并在适当报告/记录错误后为except
子句中的 a 分配默认值。
因此,为了总结并直接解决您的问题,为 if-else 语句或循环中的对象分配名称是非常好的做法,前提是:
-
if 和 else 子句中使用相同的名称,以便保证名称引用语句末尾的对象。其他
try-except-else
错误捕获可以处理函数引发的异常。 -
名称不应太短、太通用或不能明确代码意图的内容,如
a
、res
等。一个合理的名称将导致更好的可读性,并防止以后意外地将相同的名称用于其他对象,从而丢失原始名称。
让我澄清一下我在评论中的意思。
#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)
发生,否则任何对a
和b
的访问都将基于未声明的变量抛出自己的 NameError。
这在我身上发生了很多,使用自定义 Web 单元测试代码 - 我从获取或发布到 url 获得响应,并针对响应运行一堆测试。 如果原始 get/post 失败,则响应不存在,因此任何诊断(例如漂亮打印该响应的属性)都会引发异常,迫使您在异常处理程序有用之前清理内容。
因此,为了防止这种情况,我将异常处理程序中引用的任何变量初始化为 None。 当然,如果需要,您还必须防止a
被None
,例如logger("a.attr1:%s" % (getattr(a, "attr1","?")