Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. I understand that Python is a dynamically typed language and the scope of a variable may be determined at runtime.

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:

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. I am using Python 2.7.x. In case there is a local variable, an error is printed.

like image 539
Corrupted MyStack Avatar asked Jul 17 '15 03:07

Corrupted MyStack


2 Answers

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.

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.

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 (i.e. modules). The technique can be extended to cover other opcodes such as STORE_GLOBAL and DELETE_GLOBAL.

All that said, I don't see straight-forward way to detect side-effects.

like image 115
Raymond Hettinger Avatar answered Oct 20 '22 06:10

Raymond Hettinger


There is no implicit None in the sense you mean. 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
... 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
like image 24
Roshan Mathews Avatar answered Oct 20 '22 05:10

Roshan Mathews