繁体   English   中英

在Fortran的Python中是否存在等效的“隐式无”?

[英]Is there an equivalent in Python of Fortran's “implicit none”?

在Fortran中有一个语句Implicit none在未声明但使用局部变量时抛出编译错误。 我知道Python是一种动态类型语言,变量的范围可以在运行时确定。

但我想避免在忘记初始化局部变量但在主代码中使用它时发生的某些意外错误。 例如,以下代码中的变量x是全局的,即使我不打算这样做:

def test():
    y=x+2  # intended this x to be a local variable but forgot
           # x was not initialized 
    print y


x=3
test() 

所以我的问题是:有没有办法确保test()中使用的所有变量都是局部的,并且没有副作用。 我使用的是Python 2.7.x. 如果存在局部变量,则会打印错误。

所以我的问题是:有没有办法确保test()中使用的所有变量都是局部的,并且没有副作用。

有一种技术可以验证不访问全局变量。

这是一个装饰器,用于扫描函数的LOAD_GLOBAL操作码。

import dis, sys, re, StringIO

def check_external(func):
    'Validate that a function does not have global lookups'
    saved_stdout = sys.stdout
    sys.stdout = f = StringIO.StringIO()
    try:
        dis.dis(func)
        result = f.getvalue()
    finally:
        sys.stdout = saved_stdout
    externals = re.findall('^.*LOAD_GLOBAL.*$', result, re.MULTILINE)
    if externals:
        raise RuntimeError('Found globals: %r', externals)
    return func

@check_external
def test():
    y=x+2  # intended this x to be a local variable but forgot
           # x was not initialized
    print y

为了实现这一点,您需要一个可接受的全局引用(即模块)的停止列表。 该技术可以扩展到涵盖其他操作码,例如STORE_GLOBALDELETE_GLOBAL

总而言之,我没有看到直接的方法来检测副作用。

你的意思是没有隐含的None 赋值将创建一个新变量,因此拼写错误可能会在您的范围中引入新名称。

获得所需效果的一种方法是使用以下难看的黑客攻击:

def no_globals(func):
    if func.func_code.co_names:
        raise TypeError(
            'Function "%s" uses the following globals: %s' % 
            (func.__name__, ', '.join(func.func_code.co_names)))
    return func

因此,当您使用no_globals包装器声明函数test时,您将收到错误,如下所示:

>>> @no_globals
... def test():
...     y = x + 2  # intended this x to be a local variable but forgot
...                # x was not initialized 
...     print y
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in no_globals
TypeError: Function "test" uses the following globals: x
>>> 
>>> x = 3
>>> test() 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'test' is not defined

完全避免使用全局范围的变量。 如果必须,请在名称前添加一些您永远不会在本地变量名称中使用的名称。

在Python中,这完全是合法的。 事实上,它是语言的力量! 这(缺乏)错误是你可以做这样的事情的原因:

def function1():
    # stuff here
    function2()

def function2():
    pass

而在C中,您需要“转发声明” function2

有一些静态语法检查器(如flake8 )用于Python,它可以完成大量工作来捕获错误和错误的样式,但这不是错误,并且它不会被这样的检查器捕获。 否则,这样的事情将是一个错误:

FILENAME = '/path/to/file'
HOSTNAME = 'example.com'

def main():
    with open(FILENAME) as f:
        f.write(HOSTNAME)

或者,像这样更基本的东西将是一个错误:

import sys
def main():
    sys.stdout.write('blah')

您可以做的最好的事情是使用不同的命名约定(如ALL_CAPS)进行模块级变量声明。 另外,养成将所有代码放在函数中的习惯(没有模块级逻辑),以防止变量泄漏到全局命名空间。

如果您真的担心这个,可以尝试以下方法:

def test():
    try:
        x
    except:
        pass
    else:
        return
    y = x+2
    print y

但是我建议在编写一个函数时要注意,在分配它们之前不要尝试引用它们。 如果可能,尝试单独测试每个功能,并使用各种精心定义的输入和预期输出。 有各种各样的测试套件和策略 ,更不用说简单的assert关键字了。

有没有办法确保test()中使用的所有变量都是局部的,并且没有副作用。

不。该语言不提供此类功能。

有内置的locals()函数。 所以你可以写:

y = locals()['x'] + 2

但我无法想象有人认为这是一种改进。

要确保使用正确的变量,您需要限制查找的范围。 在函数内部,Python将查找在行中定义的参数,然后是argskwargs 在那些之后,它将在功能之外看。 如果函数依赖于在其他地方改变的全局变量,这可能会导致恼人的错误。

为避免意外使用全局变量,您可以使用关键字参数为要使用的变量定义函数:

def test(x=None):
    y=x+2  # intended this x to be a local variable but forgot
           # x was not initialized 
    print y

x=3
test() 

我猜你不想为很多变量做这个。 但是,它将使函数停止使用全局变量。

实际上, 即使你想在函数中使用全局变量 ,我认为最好将它变为显式:

x = 2
def test(x=x):
    y=x+2  # intended this x to be a local variable but forgot
           # x was not initialized 
    print y
x=3
test() 

此示例将使用x = 2作为函数,无论后续x的全局值发生什么。 在函数内部, x固定为编译时的值。

在烧毁几次后,我开始将全局变量作为关键字参数传递。 我认为这通常被视为良好做法?

提供的解决方案很有趣,特别是使用dis.dis解决方案,但你真的在想错误的方向。 你不想写这么麻烦的代码。

  • 你是否害怕你会意外地到达全球? 然后不要写全局变量。 模块全局变量的目的主要是达到。 (在评论中我已经读过你在范围内有50个全局变量,在我看来你有一些设计错误)。

  • 如果你仍然必须有全局变量,那么要么使用命名约定(建议使用UPPER_CASE用于常量,这可能会覆盖你的情况)。

  • 如果命名约定也不是一个选项,只需将您不希望的函数放在单独的模块中的任何全局,并且不要在那里定义全局变量。 例如,定义pure_funcs并在该模块内部,在那里编写“纯”函数,然后导入该模块。 由于python具有词法范围,因此函数只能到达在它们被编写的模块的外部作用域中定义的变量(当然还有本地或内置函数)。 像这样的东西:

     # Define no globals here, just the functions (which are globals btw) def pure1(arg1, arg2): print x # This will raise an error, no way you can mix things up. 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM