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)符号而不是a
,f
,g
,x
,y
和condition
。使用多个斜杠会使代码变得丑陋和混乱。
我不想用默认的函数之一来初始化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的基础。
- python中没有变量声明/初始化。像
a = f(x)
这样的表达式只是一个将f返回的对象命名为a
的方案。 namea
可以在以后用于命名任何其他对象,无论其类型是什么。见this答案。 - python中的块是模块的主体,类或函数。在这些对象中定义/命名的任何内容在以后的代码中都可见,直到块结束。循环或if-else不是块。因此,在循环外部定义的任何名称或if / else将在内部可见,反之亦然。见this。
global
和nonlocal
对象有点不同。 python中没有let
,因为这是默认行为。
在您的情况下,唯一的问题是如何在代码中进一步使用a
。如果您的代码需要f
或g
返回的对象类型,除非出现错误,否则它应该正常工作。因为if或else中的至少一个应该在正常操作中运行,所以a
将引用某种对象(如果名称在if和else中是不同的那将是一个问题)。如果要确保后续代码不会中断,可以使用try-except-else
捕获函数生成的任何错误,并在适当报告/记录错误后为except
子句中的a分配默认值。
因此,总结并直接解决您的问题,为if-else语句或循环内的对象分配名称是非常好的做法:
- 在if和else子句中使用相同的名称,以便保证名称引用语句末尾的对象。额外的
try-except-else
错误捕获可以处理由函数引发的异常。 - 这些名称不应该太短,一般或不能使代码的意图明确像
a
,res
等。一个明智的名称将导致更好的可读性,并防止意外使用相同的名称以后为其他一些对象失去原来的。
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)
发生,否则任何对a
和b
的访问都会根据那些未声明的变量抛出自己的NameError。
这种情况发生在我身上很多,使用自定义web单元测试代码 - 我从一个获取或发布到网址得到一个响应,我对响应进行了一系列测试。如果原始的get / post失败,则响应不存在,因此任何诊断(例如,相当打印响应的属性)都会引发异常,从而迫使您在异常处理程序有用之前进行清理。
因此,为了防止这种情况,我将异常处理程序中引用的任何变量初始化为None。当然,如果需要,你还必须防范a
是None
,像logger("a.attr1:%s" % (getattr(a, "attr1","?")
这样的东西
以上是关于Python:我应该避免在块内部对变量进行初始化吗?的主要内容,如果未能解决你的问题,请参考以下文章
Python 2.7 - 这是对 __metaclass__ 的有效使用吗? [关闭]
请问一下python里的pycurl初始化,pycurl.Curl()必须能够联网才能初始化正确吗? 我本想在局域网内部测试。