Question about name scope

O

Olive

I am learning python and maybe this is obvious but I have not been able
to see a solution. What I would like to do is to be able to execute a
function within the namespace I would have obtained with from <module>
import *

For example if I write:

def f(a):
return sin(a)+cos(a)

I could then do:

from math import *

f(5)

But I have polluted my global namespace with all what's defined in
math. I would like to be able to do something like "from math import *"
at the f level alone.

The main reason for this is the sympy module for CAS (computer algebra).
It reimplement a lot of functions and define all the letters as symbolic
variables. Writing sympy.<function> everywhere is inconvenient.
Importing all the symbols in the global namespace would lead to name
clash. It would be nice if I could import all the sympy names but for a
given function only.

Olive
 
R

Rick Johnson

But I have polluted my global namespace with all what's defined in
math. I would like to be able to do something like "from math import *"
at the f level alone.

Seeing is believing!
dir() ['__builtins__', '__doc__', '__name__', '__package__']
from math import *
dir()
['__builtins__', '__doc__', '__name__', '__package__', 'acos',
'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil',
'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp',
'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum',
'gamma', 'hypot', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10',
'log1p', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan',
'tanh', 'trunc']
================================ RESTART ================================
dir() ['__builtins__', '__doc__', '__name__', '__package__']
from math import sin, cos
dir() ['__builtins__', '__doc__', '__name__', '__package__', 'cos', 'sin']
 
D

Dave Angel

I am learning python and maybe this is obvious but I have not been able
to see a solution. What I would like to do is to be able to execute a
function within the namespace I would have obtained with from<module>
import *

For example if I write:

def f(a):
return sin(a)+cos(a)

I could then do:

from math import *

f(5)

But I have polluted my global namespace with all what's defined in
math. I would like to be able to do something like "from math import *"
at the f level alone.

The main reason for this is the sympy module for CAS (computer algebra).
It reimplement a lot of functions and define all the letters as symbolic
variables. Writing sympy.<function> everywhere is inconvenient.
Importing all the symbols in the global namespace would lead to name
clash. It would be nice if I could import all the sympy names but for a
given function only.

Olive
Start by specifying python version and operating system.

I tried your experiment using Python 2.7 and Linux 11.04


def f(a):
from math import sin, cos
return sin(a) + cos(a)

print f(45)

Does what you needed, and neatly. The only name added to the global
namspace is f, of type function.

I was a bit surprised that using from math import * inside the
function worked, but it generates a warning:
olive.py:2: SyntaxWarning: import * only allowed at module level
def f(a):

I normally avoid any use of the "from XX import *" form, as it pollutes
the global name space. The only exception is when a library writer
documents that this is the "normal" way to interface to it. In this
case, he usually defines just a few things that are visible this way)

What I do is put a single import math at the top of my source file, and
use math.sin, and math.cos where needed. Occasionally, I'll use
something like:

from math import sin,cos

at the top, so I know just which symbols I'm defining.

How about:

import math

def f(a):
sin = math.sin
cos = math.cos
return sin(a) + cos(a)

print f(45)

This lets you explicitly use the sin and cos names inside the function,
by defining them at entry to the function.
 
C

Chris Rebert

I am learning python and maybe this is obvious but I have not been able
to see a solution. What I would like to do is to be able to execute a
function within the namespace I would have obtained with  from <module>
import *

For example if I write:

def f(a):
       return sin(a)+cos(a)

I could then do:

from math import *

f(5)

But I have polluted my global namespace with all what's defined in
math. I would like to be able to do something like "from math import *"
at the f level alone.

The main reason for this is the sympy module for CAS (computer algebra).
It reimplement a lot of functions and define all the letters as symbolic
variables. Writing sympy.<function> everywhere is inconvenient.
Importing all the symbols in the global namespace would lead to name
clash. It would be nice if I could import all the sympy names but for a
given function only.

Don't think that's possible. Best alternative I can think of would be:

import sympy as s
def f(a):
return s.sin(a) + s.cos(a)

Cheers,
Chris
 
E

Ethan Furman

Olive said:
I am learning python and maybe this is obvious but I have not been able
to see a solution. What I would like to do is to be able to execute a
function within the namespace I would have obtained with from <module>
import *

For example if I write:

def f(a):
return sin(a)+cos(a)

I could then do:

from math import *

f(5)

But I have polluted my global namespace with all what's defined in
math. I would like to be able to do something like "from math import *"
at the f level alone.

If you are using Python 2.x you can do:

def f(a):
from sympy import *
return a(a) + d(a)

Python 3 does not allow * imports in functions, however, so you would
need to do:

def f(a):
from sympy import a,b,c,d,e,f,g,h,i,j,k,l,m
from sympy import n,o,p,q,r,s,t,u,v,w,x,y,z
return z(a) / f(a) + o(a)

Obviously, only import the functions you are actually going to use. ;)

~Ethan~
 
M

Mel Wilson

Dave said:
I tried your experiment using Python 2.7 and Linux 11.04


def f(a):
from math import sin, cos
return sin(a) + cos(a)

print f(45)

Does what you needed, and neatly. The only name added to the global
namspace is f, of type function.

I was a bit surprised that using from math import * inside the
function worked, but it generates a warning:
olive.py:2: SyntaxWarning: import * only allowed at module level
def f(a):

I guess they want local symbols in functions to be pre-compiled. Similar to
the way you can't usefully update the dict returned by locals(). Strangely,
I notice that

Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information..... exec x
.... exec 'print a'
....
works, but I really cannot explain why.

Mel.
 
I

Ian Kelly

I guess they want local symbols in functions to be pre-compiled.  Similar to
the way you can't usefully update the dict returned by locals().  Strangely,
I notice that

Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information....   exec x
...   exec 'print a'
...
works, but I really cannot explain why.

I am not a dev, but I believe it works because assigning to locals()
and assigning via exec are not the same thing. The problem with
assigning to locals() is that you're fundamentally just setting a
value in a dictionary, and even though it happens to be the locals
dict for the stack frame, Python can't figure out that it should go
and update the value of the optimized local to match. exec, on the
other hand, compiles and executes an actual STORE_NAME operation. Of
course, if the particular local variable hasn't been optimized by the
compiler, then updating locals() works just fine (although you
probably should not rely on this):
.... locals()[x] = y
.... print locals()[x]
.... exec 'print ' + x
....42
42

Another interesting thing to note is that the print in your example
doesn't even need to be in a second exec, which I believe works
because the presence of any exec statement disables global variable
optimizations for the function. Compare:
.... locals()['a'] = 4
.... print a
....Traceback (most recent call last):
File "<stdin>", line 1, in <module>
.... locals()['a'] = 4
.... print a
.... exec x
....4

And while we're on the subject, here's a nicely obscure syntax error:
.... def g():
.... print x
.... exec x
....
File "<stdin>", line 4
SyntaxError: unqualified exec is not allowed in function 'f' it
contains a nested function with free variables

Cheers,
Ian
 
E

Ethan Furman

Ian said:
I am not a dev, but I believe it works because assigning to locals()
and assigning via exec are not the same thing. The problem with
assigning to locals() is that you're fundamentally just setting a
value in a dictionary, and even though it happens to be the locals
dict for the stack frame, Python can't figure out that it should go
and update the value of the optimized local to match. exec, on the
other hand, compiles and executes an actual STORE_NAME operation. Of
course, if the particular local variable hasn't been optimized by the
compiler, then updating locals() works just fine (although you
probably should not rely on this):
... locals()[x] = y
... print locals()[x]
... exec 'print ' + x
...42
42

Definitely should rely on it, because in CPython 3 exec does not
un-optimize the function and assigning to locals() will not actually
change the functions variables.

~Ethan~
 
I

Ian Kelly

Definitely should rely on it, because in CPython 3 exec does not un-optimize
the function and assigning to locals() will not actually change the
functions variables.

Well, the former is not surprising, since exec was changed from a
statement to a built-in. I don't see any difference in the way
locals() behaves, though:

Python 3.2 (r32:88445, Feb 20 2011, 21:29:02) [MSC v.1500 32 bit (Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information..... locals()[x] = y
.... print(vars())
.... exec('print(' + x + ')')
....{'y': 42, 'x': 'a', 'a': 42}
42

That still seems to work as I described it. You couldn't directly
reference it as 'a', though, since the result would be either that it
would try to look up a global with that name, or the compiler would
consider it a local, optimize it, and then you could no longer assign
it via locals().

Cheers,
Ian
 
E

Ethan Furman

Ian said:
Definitely should rely on it, because in CPython 3 exec does not un-optimize
the function and assigning to locals() will not actually change the
functions variables.

Well, the former is not surprising, since exec was changed from a
statement to a built-in. I don't see any difference in the way
locals() behaves, though:

Python 3.2 (r32:88445, Feb 20 2011, 21:29:02) [MSC v.1500 32 bit (Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information.... locals()[x] = y
... print(vars())
... exec('print(' + x + ')')
...{'y': 42, 'x': 'a', 'a': 42}
42

That still seems to work as I described it. You couldn't directly
reference it as 'a', though, since the result would be either that it
would try to look up a global with that name, or the compiler would
consider it a local, optimize it, and then you could no longer assign
it via locals().

Cheers,
Ian

--> def f(x, y):
.... locals()[x] = y
.... print(vars())
.... exec('print (' + x + ')')
.... print(x)
....
--> f('a', 42)
{'y': 42, 'x': 'a', 'a': 42}
42
a

Indeed -- the point to keep in mind is that locals() can become out of
sync with the functions actual variables. Definitely falls in the camp
of "if you don't know *exactly* what you are doing, do not play this way!"

~Ethan~
 
I

Ian Kelly

--> def f(x, y):

...     locals()[x] = y
...     print(vars())
...     exec('print (' + x + ')')
...     print(x)
...
--> f('a', 42)

{'y': 42, 'x': 'a', 'a': 42}
42
a

Indeed -- the point to keep in mind is that locals() can become out of sync
with the functions actual variables.  Definitely falls in the camp of "if
you don't know *exactly* what you are doing, do not play this way!"

Sure, but that's not actually out of sync. The argument of your exec
evaluates to 'print (a)'. You get two different results because
you're actually printing two different variables. You can get the
dict temporarily out of sync:
.... frob = None
.... loc = locals()
.... loc[x] = y
.... print(loc)
.... print(locals())
.... print(loc)
....{'y': 42, 'x': 'frob', 'frob': 42, 'loc': {...}}
{'y': 42, 'x': 'frob', 'frob': None, 'loc': {...}}
{'y': 42, 'x': 'frob', 'frob': None, 'loc': {...}}

In this case, 'frob' is updated to 42 in the dict, but the optimized
local is not updated. Calling locals() again refreshes the dict.
 
E

Ethan Furman

Ethan said:
Ian said:
I am not a dev, but I believe it works because assigning to locals()
and assigning via exec are not the same thing. The problem with
assigning to locals() is that you're fundamentally just setting a
value in a dictionary, and even though it happens to be the locals
dict for the stack frame, Python can't figure out that it should go
and update the value of the optimized local to match. exec, on the
other hand, compiles and executes an actual STORE_NAME operation. Of
course, if the particular local variable hasn't been optimized by the
compiler, then updating locals() works just fine (although you
probably should not rely on this):
def f(x, y):
... locals()[x] = y
... print locals()[x]
... exec 'print ' + x
...
f('a', 42)
42
42

Definitely should rely on it, because in CPython 3 exec does not
un-optimize the function and assigning to locals() will not actually
change the functions variables.


Ouch, that should have been *not* rely on it; not because it doesn't
work (exec uses locals() if one is not specified), but because it is
easy for the names in the function to get out of sync with the names in
the functions locals() (or __dict__).

~Ethan~
 
E

Ethan Furman

Ian said:
Sure, but that's not actually out of sync. The argument of your exec
evaluates to 'print (a)'. You get two different results because
you're actually printing two different variables.

Ah -- thanks, I missed that.

You can get the dict temporarily out of sync:
... frob = None
... loc = locals()
... loc[x] = y
... print(loc)
... print(locals())
... print(loc)
...{'y': 42, 'x': 'frob', 'frob': 42, 'loc': {...}}
{'y': 42, 'x': 'frob', 'frob': None, 'loc': {...}}
{'y': 42, 'x': 'frob', 'frob': None, 'loc': {...}}

In this case, 'frob' is updated to 42 in the dict, but the optimized
local is not updated. Calling locals() again refreshes the dict.

I'm not sure what you mean by temporary:

--> def f(x, y):
.... frob = None
.... loc = locals()
.... loc[x] = y
.... print(loc)
.... print(locals())
.... print(loc)
.... print(locals())
....
-->
--> f('frob', 19)
{'y': 19, 'x': 'frob', 'frob': 19}
{'y': 19, 'x': 'frob', 'frob': None, 'loc': {...}}
{'y': 19, 'x': 'frob', 'frob': None, 'loc': {...}}
{'y': 19, 'x': 'frob', 'frob': None, 'loc': {...}}

Seems to be stuck that way.

Here is a better example I was thinking of:

--> def f(x, y):
.... locals()[x] = y
.... locals()['x'] = 17
.... print(locals())
.... print(x)
.... print(y)
....
--> f('a', 42)
{'y': 42, 'x': 'a', 'a': 42}
a
42

So locals() was updated with 'a', but not with the assignment to 'x'.
And of course, if we tried to 'print(a)' we'd get a NameError.

~Ethan~
 
I

Ian Kelly

I'm not sure what you mean by temporary:

--> def f(x, y):

...     frob = None
...     loc = locals()
...     loc[x] = y
...     print(loc)
...     print(locals())
...     print(loc)
...     print(locals())
...
-->
--> f('frob', 19)
{'y': 19, 'x': 'frob', 'frob': 19}
{'y': 19, 'x': 'frob', 'frob': None, 'loc': {...}}
{'y': 19, 'x': 'frob', 'frob': None, 'loc': {...}}
{'y': 19, 'x': 'frob', 'frob': None, 'loc': {...}}

Seems to be stuck that way.

The first print is the one that is incorrect. It suggests that the
local 'frob' has been changed to 19 as it has in the dict, but the
actual value of the local is still None. The second print on
accurately reflect that.
 
E

Ethan Furman

Ethan said:
Ethan said:
Ian said:
I am not a dev, but I believe it works because assigning to locals()
and assigning via exec are not the same thing. The problem with
assigning to locals() is that you're fundamentally just setting a
value in a dictionary, and even though it happens to be the locals
dict for the stack frame, Python can't figure out that it should go
and update the value of the optimized local to match. exec, on the
other hand, compiles and executes an actual STORE_NAME operation. Of
course, if the particular local variable hasn't been optimized by the
compiler, then updating locals() works just fine (although you
probably should not rely on this):

def f(x, y):
... locals()[x] = y
... print locals()[x]
... exec 'print ' + x
...
f('a', 42)
42
42

Definitely should rely on it, because in CPython 3 exec does not
un-optimize the function and assigning to locals() will not actually
change the functions variables.


Ouch, that should have been *not* rely on it; not because it doesn't
work (exec uses locals() if one is not specified), but because it is
easy for the names in the function to get out of sync with the names in
the functions locals() (or __dict__).

I should stop answering now :( Ignore the __dict__ comment, it is
incorrect.

~Ethan~
 
E

Ethan Furman

Ian said:
I'm not sure what you mean by temporary:

--> def f(x, y):

... frob = None
... loc = locals()
... loc[x] = y
... print(loc)
... print(locals())
... print(loc)
... print(locals())
...
-->
--> f('frob', 19)
{'y': 19, 'x': 'frob', 'frob': 19}
{'y': 19, 'x': 'frob', 'frob': None, 'loc': {...}}
{'y': 19, 'x': 'frob', 'frob': None, 'loc': {...}}
{'y': 19, 'x': 'frob', 'frob': None, 'loc': {...}}

Seems to be stuck that way.

The first print is the one that is incorrect. It suggests that the
local 'frob' has been changed to 19 as it has in the dict, but the
actual value of the local is still None. The second print on
accurately reflect that.

Ah. Thanks for the explanations.

~Ethan~
 
S

Steven D'Aprano

Indeed -- the point to keep in mind is that locals() can become out of
sync with the functions actual variables. Definitely falls in the camp
of "if you don't know *exactly* what you are doing, do not play this
way!"

And if you *do* know exactly what you are doing, you will probably decide
not to play this way also!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top