Rainyboy 发表于 2011-1-10 16:03

由两个看似等效的main函数说开去

我们知道,在一个python脚本文件(.py)中,要指定程序入口点的话,通常由如下的判断开始:if __name__ == '__main__':而不是在C/C++中的:
main(){}
也不是C#/JAVA的:
class SomeClass{      static int main(){}}
这样,在Python中的main函数的模拟通常有两种方式。要不是今天遇到的情况,我甚至没有意识到,这两种情况实际上是很不一样的!

方式(一),人为构造main()函数:
def main():
    a = 1;
    b = 3;
    print (a,b)

if __name__ == '__main__':
    main();
方式(二),直接在条件分支后写代码:
if __name__ == '__main__':
    a = 1;
    b = 3;
    print (a,b)

那么,这两种方式,有什么不一样呢?对我而言,按C/C++/C#的语法,语句块中声明的变量,其作用域只在语句块的内部,例如下面的C#的代码:
if( _name_.compareto("__main___") == 0){    int a = 1;    int b = 3;    SomeFunction();}
显然,变量a和b是一个局部变量,其作用域不可能超出两个大括号的范围,在大括号的范围之外,在SomeFunction()的内部,都是不可见的。

我们再来看按照第二种python中的程序入口点写法,写出来的某代码:
def SomeFunction():
    print (a,b)
   
if __name__ == '__main__':
    a = 1;
    b = 3;
    SomeFunction();程序仍然输出了(1,3),这多少有些让我吃惊,因为按照上述的C/C++/C#思维,是不可能发生这样的事情的,即存在于语句块内部的局部变量,在语句块内调用的函数中居然可见。那么只有一种可能,即a,b实际上被声明为了全局变量,作为对比,如果按照第一种python程序入口点的写法,写出来的代码为:

def SomeFunction():
    print (a,b)
   
def main():
    a = 1;
    b = 3;
    SomeFunction();
   
if __name__ == '__main__':
    main();运行时报错:
Traceback (most recent call last):
File "D:/My Documents/Python/learning/para.py", line 10, in <module>
    main();
File "D:/My Documents/Python/learning/para.py", line 7, in main
    SomeFunction();
File "D:/My Documents/Python/learning/para.py", line 2, in SomeFunction
    print (a,b)
NameError: global name 'a' is not defined


这不得不让我们反思,为什么在C/C++/C#中,为什么要限制语句块中变量的作用域?实际上,考察下列代码,就明白了:
# -*- coding: cp936 -*-
def SomeFunction(a,b):
    print (a,b)
   
def main():
    c = int(raw_input(u'输入一个整数:'));
    if c == 1:
      a = 1;
      b = 3;
    SomeFunction(a,b);
   
if __name__ == '__main__':
    main();
按如下两种方式输入,程序的输出是:

>>> ================================ RESTART ================================
>>>
输入一个整数:1
(1, 3)
>>> ================================ RESTART ================================
>>>
输入一个整数:2

Traceback (most recent call last):
File "D:/My Documents/Python/learning/para.py", line 13, in <module>
    main();
File "D:/My Documents/Python/learning/para.py", line 10, in main
    SomeFunction(a,b);
UnboundLocalError: local variable 'a' referenced before assignment
>>>

在某些情形下(输入整数为c=2时),变量(a和b)错过了创建的代码,而后续的代码却需要使用这些可能错过初始化的变量,程序在运行期报错!

作为一个非常喜欢C/C++/C#的程序爱好者,我认为程序语言的设计,程序员对算法的编码,都应当尽量减小运行期错误,尽量使用严格的语法、优良的设计,将程序的错误暴露在编译期。这里有一个例子,为了克服C/C++/C#语言中的赋值(=)和相等(==)的混淆,甚至建议在条件判断中采用:
if (3 == a){}这样,当代码被误写为:
if(3 = a){}程序会在编译器报错,避免了程序在运行时的出现奇怪错误的一种可能性。

_________________________
这就是我想说明的问题:

1)在python中,由于语句块中的变量不再被限制,导致了第二种程序入口点代码的写法无意中声明了全局变量,而对于C/C++/C#程序员转型而来的Python程序员,可能没有意识到这些全局变量的存在;

2)在python中,由于语句块中的变量不再被限制,导致了由变量作用域带来的运行期问题,会延迟程序BUG的发现时机,实际上是不利于团队开发和大型程序设计的。

_________________________
一家之言,欢迎大家批评!

mayaview 发表于 2013-12-14 14:40

两种不同的作用域管理方式吧,各有优势。
页: [1]
查看完整版本: 由两个看似等效的main函数说开去