Home  >  Article  >  Backend Development  >  python不区分变量定义和赋值,是否是一个设计上的缺陷?

python不区分变量定义和赋值,是否是一个设计上的缺陷?

WBOY
WBOYOriginal
2016-06-06 16:22:291270browse

比如:

def foo():
    a = 10
    def foo2():
        a = 20

回复内容:

这不是王垠说的么,王垠专业就是研究编程语言。研究语言的博士生针对各种语言特性做的分析,对我们都是有很多启发的。但你说的这种外面有个a,函数里面又有个a的情况,我想问问,其实实际开发中,是不是每天都要处理内外层变量名字冲突的问题?我们有两种选择:

一种做法是象王垠说的:区别变量声明和赋值,就是类似声明时前面加个 var一样,
另外的做法象龟多做的:没有 var,只有在冲突的时候加一些 global nolocal之类的关键字。

哪种做法好,请你想想大多数情况下,99%的时候你是不会傻到内外用一样的名字的吧,好的编程习惯甚至全局变量和局部变量在命名上都是区别的,不会出现你说的冲突。系统设计就是需要不断的做出选择,龟多也一样做出了他的选择,让你 99%的情况下都不需要为每个变量去写一个 var声明去,以此保证语言的简洁性和优雅性。直到你碰到 1%的极端的情况时,用一下 nolocal global又有什么困难的呢?

然而这些对用户优雅的特性,对于写 PySonar的王垠来讲,却无外乎一种折磨,要在静态分析中间指出他们的区别来是很痛苦的,这是肯定的事情,层次越高级的语言越不容易做静态分析,所以王垠被恶心到了,狂喷 global nolocal也是可以理解的。

所以别人云亦云,多想想说这些话是在什么背景下说出来的,这些客观条件和自己所处的情况相同否,再做出自己的判断。
------------------ 补充一个目前大家都没有提到的方面。

题主,先不论是不是Python 的设计缺陷,你通过以上例子得出的那个结论,是存在以偏概全的问题的。题主的例子表达的观点实际上为:Python中,在没有声明的情况下,子函数中的同名变量无法改变外围同名变量。不过,这种网上广为流传的说法是错误的。

几行代码可以说明这个问题:
<code class="language-python"><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span>
    <span class="n">a_int</span><span class="o">=</span><span class="mi">10</span>
    <span class="n">a_list_with_method</span><span class="o">=</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">10</span><span class="p">]</span>
    <span class="k">def</span> <span class="nf">foo2</span><span class="p">():</span>
        <span class="n">a_int</span><span class="o">=</span><span class="mi">20</span>
        <span class="n">a_list_with_method</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">11</span><span class="p">)</span>
    <span class="n">foo2</span><span class="p">()</span>
    <span class="k">print</span><span class="p">(</span><span class="n">a_int</span><span class="p">)</span>    <span class="c">#Python2.x 请用print a_int</span>
    <span class="k">print</span><span class="p">(</span><span class="n">a_list_with_method</span><span class="p">)</span>    <span class="c">#Python2.x 请用print a_list_with_method</span>


<span class="n">foo</span><span class="p">()</span>
</code>
首先,不要求完全精确的情况下,Python 其实可以说是“没有变量”,有的只是“名字绑定”,每一个名字可以视为一个实例(instance)的引用,变量只是大家沿用了更熟悉的概念而已。也就是说,`a = 20` 这句代码里,`a` 只是一个名字(引用)。
然后,回到题主的代码,这是一个 Python 的名字搜索机制的问题,因为 Python2.x 的实现是本地范围找不到的话就去全局范围找,那么就会出现题主说的“在foo2中没有办法改变foo中a的值”的问题,这个问题后来在 Python3.x 中通过引入 `nonlocal` 解决了。
最后,“python不区分变量定义和赋值,是否是一个设计上的缺陷?”,这是一个伪问题,开篇说过了 Python 其实可以说是“没有变量”,所以何来“定义”之说,说到“缺陷”就更是强加的罪名了。 python的closure设计的确是有缺陷的。不只是这个地方,还有引用传参却又没有强制只读。
但是,我跟你说,这是个feature,不是缺陷。python设计哲学根本不鼓励你用词法作用域这种花式语法,按照python的逻辑,你应该老老实实用自变量导入所有的参数。那些nonlocal和global都是后门,不推荐使用。
所谓的python设计哲学,就是简单直接,遵从感觉,不向硬件妥协,也不向数学屈膝。作用域这种复杂的数学问题,明智的方法是绕开它,不要去思考。写个python还要思考作用域,那跟写C++有什么区别?当然,如果你一定要思考这种问题,C++欢迎你(逃 这个不能算缺陷吧...想实现这种功能应该是有其它的办法来实现的。对于有些语言,比如C来说,函数是不可以嵌套的。

我倒是觉得Python不区分声明和赋值的话,有些地方变量名拼写错了,运行也可能不报错,然后就出现各种神奇的bug了。 因为python中所有东西都是对象,赋值其实只是引用绑定,所以这个情况某种意义上是必然的。

这也与python遵循的基本原则有关。如果遵守简单唯一表示的原则,那么并没有什么不方便的地方;如果说这个原则不好,那也没办法了……
在foo2中没有办法改变foo中a的值,这能否说因为python不区分变量定义和赋值从而导致的一个缺陷?python3引入的nonlocal恰好证明了这一点?
我想说的是,你从一个错误的论据导出了一个正确的结论。。

Python的name rebinding(assign to)机制的确有缺陷,只能rebind到local和module-global,而不能rebind到outer scope。

但Python是有变量声明的,比如global语句就是用来绑定global变量的,而local声明是隐式的。Python 3新加的nonlocal修补了不能绑定到outer scope的问题。

PEP 3104 -- Access to Names in Outer Scopes 你确定是设计的缺陷,而不是美德(merit)? 我觉得应该是缺陷吧,考虑如下情形
variable = 10
bla bla bla...
varable = 3//此处由于拼写,少打了个i,认为是新变量
variable += 16

计算错误 初学Python,有几年c++经验,我说几句我理解的。
对于任何变量来说,他都应该遵循最小作用域,既然Python不区分赋值既创建,那好那我就创建吧!别的我不管,你想用其他作用域的,好吧!你指定吧!
至于说为什么赋值既创建,可能是因为Python没有强制要求声明数据类型的缘故吧!至于说优化,默认是引用形式,只是内存性能方面的优化吧!
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn