0

I have a conditional import in a self-initialized instance of a superclass, but subclass cannot see the module (python 2.7):

class A(object):
    def __init__(self, arg1):
        self.attr1 = self.method1(arg1)

    def method1(self, arg1):
        if arg1 == 'foo':
           import amodule
           return amodule.method1()
        else:
            return 'not a dependency on foo'


class B(A):
    def __init__(self, arg1):
        super(B, self).__init__(arg1)
        if arg1 == 'foo':
            self.attr2 = self.method2(self.attr1)

    def method2(self, attr1):
        return amodule.method2()

if __name__=='__main__':
    b = B("foo")
    print b.attr2

This throws NameError: global name 'amodule' is not defined. a = A("foo") works just fine

Shouldn't the super call have executed import amodule in this case? (And using import should have put the module into globals?)

cowbert
  • 3,212
  • 2
  • 25
  • 34
  • 1
    `amodule` is a *local variable*. Why would you think you would have access to it in `method2`? Also, the `import` statement does *not* bind the module name in the global namespace, but in whatever namespace the import statement is. Again, this would be *local to `method1`*. – juanpa.arrivillaga Jul 31 '17 at 17:30
  • doesn't `import` add /amodule/ to the global namespace of the currently executing module? (\_\_main\_\_)? – cowbert Jul 31 '17 at 17:32
  • 1
    No, it doesn't Check this yourself `def f(): import sys`, then call `f()` and see if `sys` is in the global namespace, it isn't (unless it was already). – juanpa.arrivillaga Jul 31 '17 at 17:32
  • @juanpa.arrivillaga: `A.__init__` calls `method1`. – user2357112 Jul 31 '17 at 17:34
  • @user2357112 ah, yes, you are right. I missed that. Anyway, the overall point still stands: `amodule` is *local to `method1`* – juanpa.arrivillaga Jul 31 '17 at 17:35
  • Why don't you just encapsulate the conditional `import` into it's own method, that *return the module*. Or maybe sets it to an instance variable. Honestly, not sure what would be prettier... – juanpa.arrivillaga Jul 31 '17 at 17:37
  • It looks like I was mislead by https://stackoverflow.com/questions/6861487/importing-modules-inside-python-class#comment42531679_6861854 when @agf mentioned global namespacing of the module by `import`. – cowbert Jul 31 '17 at 17:41

3 Answers3

2

Doesn't import add /amodule/ to the global namespace of the currently executing module? (__main__)?

No, the module is added to sys.modules but if it was imported locally then you won't have any references to it anymore. i.e the name amodule is now gone.

You could still access the module using sys.modules:

def method2(self, attr1):
    import sys
    return sys.modules['amodule'].method2()

Or you could import it using import amodule again and it will be picked up from sys.modules.


# Here b.py contains
# print('Module b was imported')

def func1():
    print('inside func1')
    import b

def func2():
    print('inside func2')
    import sys
    print(sys.modules['b'])
    import b


def func3():
    print('inside func3')
    import b
    import sys
    print('Deleted b')
    del sys.modules['b']
    import b


func1()
print()
func2()
print()
func3()

Demo:

inside func1
Module b was imported

inside func2
<module 'b' from '/Users/ashwini/py/b.py'>

inside func3
Deleted b
Module b was imported
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • I could also make it an instance object too. importing this module is a bit expensive, unfortunately. – cowbert Jul 31 '17 at 17:47
  • 1
    @cowbert Those calculations will only happen on the first import, later imports will pick the already imported module unless you delete it from `sys.modules`. Check the example in the answer. – Ashwini Chaudhary Jul 31 '17 at 17:52
0

Try putting import amodule on the first line of the program.

The reason is that amodule is imported in method1, method2 does not have access.

James
  • 1,928
  • 3
  • 13
  • 30
  • The import is conditional for a specific reason not mentioned (since it's irrelevant to the issue at hand). – cowbert Jul 31 '17 at 17:35
0

If you follow your code, you would see you don't reach method1().

When you create the object b = B(foo) You go through A.init() becuase the call for super(). However, your init of class A() doesn't include any import statements. Then the A.__init__ part is done, and you continue with B.__init__(). The next command is a call to amodule object, which wasn't imported at all.

You can add an helper method, which checks if the arg equals 'Foo' and if so import the module. Then add a call to this function in A.__init__() function.

On another note, __init__() job is to initialize variables. It shouldn't return anything.

Chen A.
  • 10,140
  • 3
  • 42
  • 61
  • Where do you see \_\_init\_\_() returning anything? It does initialize variables, their initial values should be the return values from internal methods... – cowbert Jul 31 '17 at 17:50