Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
485 views
in Technique[技术] by (71.8m points)

Disable global variable lookup in Python

In short, the question: Is there a way to prevent Python from looking up variables outside the current scope?

Details:

Python looks for variable definitions in outer scopes if they are not defined in the current scope. Thus, code like this is liable to break when not being careful during refactoring:

def line(x, a, b):
    return a + x * b

a, b = 1, 1
y1 = line(1, a, b)
y2 = line(1, 2, 3)

If I renamed the function arguments, but forgot to rename them inside the function body, the code would still run:

def line(x, a0, b0):
    return a + x * b  # not an error

a, b = 1, 1
y1 = line(1, a, b)  # correct result by coincidence
y2 = line(1, 2, 3)  # wrong result

I know it is bad practice to shadow names from outer scopes. But sometimes we do it anyway...

Is there a way to prevent Python from looking up variables outside the current scope? (So that accessing a or b raises an Error in the second example.)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Yes, maybe not in general. However you can do it with functions.

The thing you want to do is to have the function's global to be empty. You can't replace the globals and you don't want to modify it's content (becaus that would be just to get rid of global variables and functions).

However: you can create function objects in runtime. The constructor looks like types.FunctionType((code, globals[, name[, argdefs[, closure]]]). There you can replace the global namespace:

def line(x, a0, b0):
   return a + x * b  # will be an error

a, b = 1, 1
y1 = line(1, a, b)  # correct result by coincidence

line = types.FunctionType(line.__code__, {})
y1 = line(1, a, b)  # fails since global name is not defined

You can of course clean this up by defining your own decorator:

import types
noglobal = lambda f: types.FunctionType(f.__code__, {}, argdefs=f.__defaults__)

@noglobal
def f():
    return x

x = 5
f() # will fail

Strictly speaking you do not forbid it to access global variables, you just make the function believe there is no variables in global namespace. Actually you can also use this to emulate static variables since if it declares an variable to be global and assign to it it will end up in it's own sandbox of global namespace.

If you want to be able to access part of the global namespace then you'll need to populate the functions global sandbox with what you want it to see.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...