首页 >后端开发 >Python教程 >python不区分变量定义和赋值,是否是一个设计上的缺陷?

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

WBOY
WBOY原创
2016-06-06 16:22:291336浏览

比如:

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没有强制要求声明数据类型的缘故吧!至于说优化,默认是引用形式,只是内存性能方面的优化吧!
声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn