简体   繁体   English

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

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

In Fortran there is a statement Implicit none that throws a compilation error when a local variable is not declared but used. 在Fortran中有一个语句Implicit none在未声明但使用局部变量时抛出编译错误。 I understand that Python is a dynamically typed language and the scope of a variable may be determined at runtime. 我知道Python是一种动态类型语言,变量的范围可以在运行时确定。

But I would like to avoid certain unintended errors that happen when I forget to initialize a local variable but use it in the main code. 但我想避免在忘记初始化局部变量但在主代码中使用它时发生的某些意外错误。 For example, the variable x in the following code is global even though I did not intend that: 例如,以下代码中的变量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() 

So my question is that: Is there any way to ensure all variables used in test() are local to it and that there are no side effects. 所以我的问题是:有没有办法确保test()中使用的所有变量都是局部的,并且没有副作用。 I am using Python 2.7.x. 我使用的是Python 2.7.x. In case there is a local variable, an error is printed. 如果存在局部变量,则会打印错误。

So my question is that: Is there any way to ensure all variables used in test() are local to it and that there are no side effects. 所以我的问题是:有没有办法确保test()中使用的所有变量都是局部的,并且没有副作用。

There is a technique to validate that globals aren't accessed. 有一种技术可以验证不访问全局变量。

Here's a decorator that scans a function's opcodes for a LOAD_GLOBAL . 这是一个装饰器,用于扫描函数的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

To make this practical, you will want a stop list of acceptable global references (ie modules). 为了实现这一点,您需要一个可接受的全局引用(即模块)的停止列表。 The technique can be extended to cover other opcodes such as STORE_GLOBAL and DELETE_GLOBAL . 该技术可以扩展到涵盖其他操作码,例如STORE_GLOBALDELETE_GLOBAL

All that said, I don't see straight-forward way to detect side-effects. 总而言之,我没有看到直接的方法来检测副作用。

There is no implicit None in the sense you mean. 你的意思是没有隐含的None Assignment will create a new variable, thus a typo might introduce a new name into your scope. 赋值将创建一个新变量,因此拼写错误可能会在您的范围中引入新名称。

One way to get the effect you want is to use the following ugly-ish hack: 获得所需效果的一种方法是使用以下难看的黑客攻击:

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

So when you declare your function test –with the no_globals wrapper–you'll get an error, like so: 因此,当您使用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

Just avoid using globally-scoped variables at all. 完全避免使用全局范围的变量。 And if you must, prefix their names with something you'll never use in a local variable name. 如果必须,请在名称前添加一些您永远不会在本地变量名称中使用的名称。

In Python, this is quite simply entirely legal. 在Python中,这完全是合法的。 In fact, it is a strength of the language! 事实上,它是语言的力量! This (lack) of error is the reason why you can do something like this: 这(缺乏)错误是你可以做这样的事情的原因:

def function1():
    # stuff here
    function2()

def function2():
    pass

Whereas in C, you would need to "forward declare" function2 . 而在C中,您需要“转发声明” function2

There are static syntax checkers (like flake8 ) for Python that do plenty of work to catch errors and bad style, but this is not an error, and it is not caught by such a checker. 有一些静态语法检查器(如flake8 )用于Python,它可以完成大量工作来捕获错误和错误的样式,但这不是错误,并且它不会被这样的检查器捕获。 Otherwise, something like this would be an error: 否则,这样的事情将是一个错误:

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

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

Or, something even more basic like this would be an error: 或者,像这样更基本的东西将是一个错误:

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

The best thing you can do is use a different naming convention (like ALL_CAPS) for module level variable declarations. 您可以做的最好的事情是使用不同的命名约定(如ALL_CAPS)进行模块级变量声明。 Also, make it a habit to put all of your code within a function (no module-level logic) in order to prevent variables from leaking into the global namespace. 另外,养成将所有代码放在函数中的习惯(没有模块级逻辑),以防止变量泄漏到全局命名空间。

If you were really worried about this, you could try the following: 如果您真的担心这个,可以尝试以下方法:

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

But I'd recommend simply being mindful when writing a function that you don't try to reference things before assigning them. 但是我建议在编写一个函数时要注意,在分配它们之前不要尝试引用它们。 If possible, try to test each function separately, with a variety of carefully-defined inputs and intended outputs. 如果可能,尝试单独测试每个功能,并使用各种精心定义的输入和预期输出。 There are a variety of testing suites and strategies , not to mention the simple assert keyword. 有各种各样的测试套件和策略 ,更不用说简单的assert关键字了。

Is there any way to ensure all variables used in test() are local to it and that there are no side effects. 有没有办法确保test()中使用的所有变量都是局部的,并且没有副作用。

No. The language offers no such functionality. 不。该语言不提供此类功能。

There is the built in locals() function. 有内置的locals()函数。 So you could write: 所以你可以写:

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

but I cannot imagine anyone considering that to be an improvement. 但我无法想象有人认为这是一种改进。

To make sure the correct variable is used, you need to limit the scope of the lookup. 要确保使用正确的变量,您需要限制查找的范围。 Inside a function, Python will look to arguments defined in line, then to the args and kwargs . 在函数内部,Python将查找在行中定义的参数,然后是argskwargs After those, its going to look outside the function. 在那些之后,它将在功能之外看。 This can cause annoying bugs if the function depends on a global variable that gets changed elsewhere. 如果函数依赖于在其他地方改变的全局变量,这可能会导致恼人的错误。

To avoid using a global variable by accident, you can define the function with a keyword argument for the variables your going to use: 为避免意外使用全局变量,您可以使用关键字参数为要使用的变量定义函数:

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() 

I'm guessing you don't want to do this for lots of variables. 我猜你不想为很多变量做这个。 However, it will stop the function from using globals. 但是,它将使函数停止使用全局变量。

Actually, even if you want to use a global variable in the function , I think its best to make it explicit: 实际上, 即使你想在函数中使用全局变量 ,我认为最好将它变为显式:

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() 

This example will use x=2 for the function no matter what happens to the global value of x afterwards. 此示例将使用x = 2作为函数,无论后续x的全局值发生什么。 Inside the function, x is fixed to the value it had at compile time. 在函数内部, x固定为编译时的值。

I started passing global variables as keyword arguments after getting burned a couple times. 在烧毁几次后,我开始将全局变量作为关键字参数传递。 I think this is generally considered good practice? 我认为这通常被视为良好做法?

The offered solutions are interesting, especially the one using dis.dis , but you are really thinking in the wrong direction. 提供的解决方案很有趣,特别是使用dis.dis解决方案,但你真的在想错误的方向。 You don't want to write such a cumbersome code. 你不想写这么麻烦的代码。

  • Are you afraid that you will reach a global accidentally? 你是否害怕你会意外地到达全球? Then don't write globals. 然后不要写全局变量。 The purpose of module globals is mostly to be reached. 模块全局变量的目的主要是达到。 (in a comment I have read that you have 50 globals in scope, which seems to me that you have some design errors). (在评论中我已经读过你在范围内有50个全局变量,在我看来你有一些设计错误)。

  • If you still DO have to have globals, then either use a naming convention (UPPER_CASE is recommended for constants, which could cover your cases). 如果你仍然必须有全局变量,那么要么使用命名约定(建议使用UPPER_CASE用于常量,这可能会覆盖你的情况)。

  • If a naming convention is not an option either, just put the functions you don't want to reach any global in a separate module, and do not define globals there. 如果命名约定也不是一个选项,只需将您不希望的函数放在单独的模块中的任何全局,并且不要在那里定义全局变量。 For instance, define pure_funcs and inside of that module, write your "pure" functions there, and then import this module. 例如,定义pure_funcs并在该模块内部,在那里编写“纯”函数,然后导入该模块。 Since python has lexical scope, functions can only reach variables defined in outer scopes of the module they were written (and locals or built-ins, of course). 由于python具有词法范围,因此函数只能到达在它们被编写的模块的外部作用域中定义的变量(当然还有本地或内置函数)。 Something like this: 像这样的东西:

     # 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