11

I was confident to have at least some basic understanding of Python's scope system. Now I get an error and unfortunately I'm not even able to write a good code snippet for reproduction so far. I tried to reproduce it in a new small project, but everything works as I expect there :-/

I can only describe what I do and hopefully somebody detects a pattern and can tell me what possibly goes wrong here.

At first there is a python file x.py which implements a class X.

In some other python file, there is the following string:

code="""
...
from x import X
...
class Y(X): # does not crash here, ...
    def __init__(self):
        X.__init__(self) # ... but here
        ...
foo=Y()
"""

You can assume that python is able to find the x module. At some place, I try to execute that:

exec(code, globals(), locals())

And now I get the NameError. It tells me that X is not defined when it tries to call it's constructor. It was obviously defined a few lines above.

If I modify Y.__init__ with adding from x import X as first line, it works. But why the hell do I have to import it again there?

As already indicated, the actual code is more complex and does more things. In an unlucky case, my posting does not even show the part which actually leads to the problem. But maybe you have some general ideas, how one can get such a behaviour.

13
  • Just out of curiosity, why call X.__init__(self) and not X(self) Commented Apr 30, 2015 at 23:18
  • 3
    I don't immediately see the problem, and it depends what else has happened to the global namespace, but usually you'd use super(Y, self).__init__() for such a case and avoid the whole issue. Commented Apr 30, 2015 at 23:24
  • ... well, just because I wasn't aware of that shortcut. But using it doesn't change anything about the error :( Commented Apr 30, 2015 at 23:25
  • Nothing changes the global namespace between the definition of Y and the instantiation which crashes. There really really is no code inbetween. Commented Apr 30, 2015 at 23:27
  • 1
    There is nothing wrong in what you have shown. what are the lines you are not showing? Commented Apr 30, 2015 at 23:27

2 Answers 2

10

This is just a guess, because you haven't shown us enough code, and what you've shown us doesn't actually reproduce the problem, but…

If you're doing this exec inside a function, then locals() and globals() are going to be different. In which case the code will be executed as if it were inside a class definition. So, it'll be (sort of) as if you did this:

class _:
    from x import X
    class Y(X): # does not crash here, ...
        def __init__(self):
            X.__init__(self) # ... but here
    foo=Y()
del _

(I previously thought you'd have to also be doing something like Y() outside the exec, but user2357112's answer convinced me that isn't necessary.)

If that's your problem, you may be able to fix it by just calling exec(code, globals(), globals()) or exec(code, locals(), locals()). (Which one is appropriate, if either, depends on what you're actually trying to do, of course, which you haven't told us.)

Sign up to request clarification or add additional context in comments.

5 Comments

I don't quite follow this... Why would Y() work in one scope and fail in a different scope, even though Y refers to the same class in both scopes?
I think the recommendation in the last paragraph is probably right, regardless.
Yeah, you were correct with your guesses ;) Great, thank you! I call exec in my real code from inside a function. In my test code, it was not in a function, so globals() and locals() maybe returned the same thing which leads to another behavior of exec (as you mentioned).
@abarnet Ah, OK, I see you've edited the part of the answer I wasn't following, so everything makes sense now.
@JasonOrendorff: Yeah, I upvoted user2357112's answer, but mine had already been accepted, so rather than delete it I corrected it.
8

From the exec documentation:

If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.

There are good reasons for this, which I won't go into here.

Functions defined in a class definition don't look in the scope of the class definition for variable resolution. When you exec your code, it's actually executed like this:

class Dummy:
    from x import X
    ...
    class Y(X):
        def __init__(self):
            X.__init__(self)
            ...
    foo=Y()

That means this function:

def __init__(self):
    X.__init__(self)

doesn't see this variable:

from x import X

even though this bit:

class Y(X):

does see it.

10 Comments

I'm pretty sure this isn't quite right—if it were, his code as posted would raise, and it doesn't. That's why I think there has to be something else going on. My best guess, as in my answer, is that his actual code, unlike his example, is constructing Y instances outside the exec, after the dummy class definition has finished executing and its scope has gone away and X is no longer available (or only available as a class attribute on an inaccessible class). But that's just a wild guess.
@abarnert: I'm not sure what you mean when you say it doesn't raise.
Take his code, remove the ...s, create a x.py with class X: pass, and run it. No exception. Everything works. (The OP admitted from the start that his example isn't an MCVE because he's been unable to come up with a simple reproduction of the problem, and later edited it to make that even clearer: "In an unlucky case, my posting does not even show the part which actually leads to the problem.")
For that matter, just take your "as-if" code, remove the ...s, and run it. It'll execute just fine, and you can verify that Dummy.foo is an instance of __main__.Dummy.Y. So, Y.__init__ is clearly able to see X at execution time.
@abarnert: I'm trying to run a simple example through ideone, but it's not loading. I don't have Python 3; could you link me an example?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.