0

I wrote the following Python code for illustration:

class foo:
    def foo_function(self):
      return 1

class bar:
    def bar_function(self):
        foo = foo()
        return foo.foo_function()

bar = bar()
print(bar.bar_function())

where in class bar I need to use class foo. I got the following error when I run it:

# UnboundLocalError: local variable 'foo' referenced before assignment

This error comes from the line foo = foo() where the variable and class have the same name, and it will go away if use different names, but I would like to know what happened internally, please note bar = bar() is okay.

By the way, is this the right way to use one class from another class. I use separate class because they are independent: for example, foo is to get secret stored on the AWS, and bar uses the secret in a different app not related to the AWS at all.

puravidaso
  • 1,013
  • 1
  • 5
  • 22
  • Try `b = bar()` and `f = foo()` instead to see what happens. – quamrana Jan 13 '21 at 17:35
  • @quamrana `foo = foo()` needs different names but `bar = bar()` does not, that is why I need to know what happens internally. – puravidaso Jan 13 '21 at 17:38
  • @VPfB Then how the interpreter knows in the case of `bar = bar()`? – puravidaso Jan 13 '21 at 17:40
  • @puravidaso because local `foo` "shadows" the global `foo`. I deleted my original comment, because now I see it was not clear and more space is needed to explain it fully. – VPfB Jan 13 '21 at 17:41

2 Answers2

1

The problem here is overusing the python namespace.

Each variable that is saved into memory is allocated a name to refer and retrieve it within each scope (function, global etc.)

So looking down the file from top to bottom

  1. globally the class foo is created and foo points to this class
  2. globally the class bar is created
  3. Within bar, the global namespace persists, so foo still points to the class defined beforehand.
  4. However when the class is created, the line foo = foo() tells the class that foo is a variable in the function and a new link in the namespace is created to use foo for that variable, and the link (at least within the bar class) to the foo class is lost
  5. Then bar tried to call foo, but there is an empty variable related to the name foo

Best practice is to:

  1. Avoid namespace collisions, just use different variable names
  2. use capitalization for classes i.e.
class Foo():
 def foo_func():
  return 1

foo = Foo()

Note that even though

bar = bar()

is ok and runs, you can no longer access the class bar version of bar because the name bar has been overwritten, so bar2 = bar() will fail

Try this to see what's happening with the namespace

class foo:
    def foo_function(self):
        return 1

print('after class foo')
print(str(foo))

class bar:
 def bar_function(self):

  print('in bar')
  print(str(foo))

  foo = foo()
  return foo.foo_function()

print('after class bar')
print(str(foo))
print(str(bar))

bar = bar()

print('after instantiating bar')
print(str(bar))

bar.bar_function()
NickHilton
  • 662
  • 6
  • 13
  • if "bar" class does not know "foo" class (it should as I tell Python it is a class with "foo()"), then how come it knows when I assign it to a different variable, for example, "foo_in_var=foo()"? – puravidaso Jan 13 '21 at 19:06
  • 1
    Once the line `foo=foo()` is called, then the pointer in the namespace of `foo -> class foo` is replaced within `bar` by `foo -> variable foo`. And so `bar` doesn't know about the `class foo` any more. When you do `foo_in_var=foo()` then namespace with `bar` is `foo -> class foo` and `foo_in_var -> implementation of foo()` still – NickHilton Jan 13 '21 at 19:59
  • What you said above should apply to `bar = bar()` as well, why it doesn't? – puravidaso Jan 13 '21 at 20:30
  • 1
    Good question. I think its because of the way a class is compiled basically packaging up all the code at once, vs. lines run in a file executing as they go. You should look at the link in wankata's answer to https://docs.python.org/3/faq/programming.html?highlight=unboundlocalerror#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value which explains in more detail – NickHilton Jan 13 '21 at 20:49
  • 1
    See this question as well: https://stackoverflow.com/questions/42451827/python-local-variable-compile-principle. The basic explanation is that once the python compiler sees `foo = foo()` in the `bar` class, it categories `foo` as a local variable and cannot access the global variable `foo`. Whereas when the line `bar=bar()` is executed, the pointer to `bar ` is retrieved to call `bar()` and then reassigned in that order – NickHilton Jan 13 '21 at 20:56
0

The hilton92's answer is correct. You may want to see also the python FAQ regarding the UnboundLocalError

But the most important thing is, that what you are doing is a bad idea, because it makes your code confusing and doesn't follow any best practices guidelines.

I suggest you to read the PEP 8 -- Style Guide for Python Code

wankata
  • 855
  • 4
  • 12