1

when I use any class variable in class code blocks, I'm getting not defined error. how can I fix it ?

example

class A():
    __hidden_number__ = 5
    __no_hidden_number__ = A.__hidden_number__ + 4 # to cause error. why ?

Thanks.

Nomad
  • 918
  • 6
  • 23
  • 1
    try `__no_hidden_number__ = __hidden_number__ + 4` – Jean-François Fabre Sep 12 '17 at 20:28
  • Because `A` is not defined yet. – juanpa.arrivillaga Sep 12 '17 at 20:41
  • 1
    @juanpa.arrivillaga nope. You're in `A` scope. You're refering to `A.A`. Check my answer and test the snippet. – Jean-François Fabre Sep 12 '17 at 20:42
  • @Jean-FrançoisFabre This sounds misleading. `A` has an outer scope. If there's an `A` already in the enclosing namespace, `A.__hidden_number__` would refer to that. Inside the class declaration the *name* of the class you are currently declaring is not part of the resolution hierarchy. – dhke Sep 12 '17 at 20:51
  • @dhke: you're saying that I'm not using the correct vocabulary? possible. Can you help on that? – Jean-François Fabre Sep 12 '17 at 20:54
  • @Jean-FrançoisFabre I'm not sure, but I'd rather be precise. When you're inside the class decl, you are building a code object for the class. (`dis.disassemble()` is very helpful, here). That code object gets assigned to the name `A` in the enclosing namespace. Which is why you don't have `A` inside the class declaration, because that name hasn't gotten assigned at that point, yet. – dhke Sep 12 '17 at 20:56
  • @dhke I'll look into this disassemble thing. But have you checked my 2 code snippets? I think they speak for themselves. – Jean-François Fabre Sep 12 '17 at 20:58
  • I've tried to disassemble class construction, but could not. Not sure it can be done (as you see I hate when someone doesn't agree with me :), I mean I don't like when we don't understand each other arguments or even _facts_.)) – Jean-François Fabre Sep 12 '17 at 21:02
  • @Jean-FrançoisFabre No, but *while the class definition is being executed* the `A` namespace doesn't exist. Or, at least, it *isn't assigned to the name `A`*. This is why you need to use string-literals to provide ["forward references"](https://www.python.org/dev/peps/pep-0484/#forward-references), and why the error is a `NameError`. Someone with better knowledge of the Python data model could say better whether the namespace doesn't exist or isn't assigned to the name. To disassemble the class creation, though, try something like `dis.dis(compile("class A: pass",'test', 'exec'))` – juanpa.arrivillaga Sep 12 '17 at 21:25

2 Answers2

4

your class variable is already in A namespace.

So you just have to do:

__no_hidden_number__ = __hidden_number__ + 4

else you're trying to access A.A and it doesn't exist yet.

To illustrate what I'm stating, that "works" (but not what you want):

class A():
    class A():
        __hidden_number__ = 4
    __no_hidden_number__ = A.__hidden_number__ + 4
A()  # that works

that doesn't:

class A():
    class B():
        __hidden_number__ = 4
    __no_hidden_number__ = A.__hidden_number__ + 4  # B.__... would've worked
A()  # beeep: NameError: name 'A' is not defined

note that you should avoid to define private attributes with trailing double underscore (__xxx__). It works, but this is generally reserved for python special class members (__class__, __file__, __eq__, etc...). Name mangling (making the object "private") already occurs with 2 leading underscores.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
2

Since this might be a little unclear (the existing answer is fine), let me add to that:

class A:
    pass

is equivalent to

A = type('A', (object,), {})

What we write inside the class declaration is essentially building that (here empty) dictionary and that happens before the variable A gets assigned. And since we have --not yet-- a variable of the name A in the namespace hierarchy while the class declaration runs, A isn't available.

The class exists, it also has the type name 'A', but it hasn't gotten assigned to the variable A, yet.

The difference between the class A: pass-version and the explicit type call is syntactic sugar: When using class, everything nested inside the class is automatically namespaced into the class dictionary.

This is why this works:

class A:
   var = 1
   var2 = var + 1

But as we can see above, the local variable A that will finally hold our new class is assigned only after the class declaration has already been evaluated and thus isn't available inside of it.

If we disassemble a class decl, we get:

  import codeop, dis
  dis.disassemble(codeop.compile_command('class A:\r\n    pass'))

1           0 LOAD_CONST               0 ('A')
            3 LOAD_CONST               3 (())
            6 LOAD_CONST               1 (<code object A at 0x80076ff30, file "<input>", line 1>)
            9 MAKE_FUNCTION            0
           12 CALL_FUNCTION            0
           15 BUILD_CLASS         
           16 STORE_NAME               0 (A)
           19 LOAD_CONST               2 (None)
           22 RETURN_VALUE  

19 and 22 are added automatically, since code objects must return a value. We can ignore those for our example.

What else is happening here?

  • 0: pushes the string 'A' onto the stack. This will become the name of the class (the first argument to type()).
  • 3: pushes an empty tuple, the list of parent classes
  • 6: Loads the code object for the class declaration.
  • 9 and 12 run the class declaration code object. This is what e.g. does our (class) variable assignments. It returns the class dictionary.
  • 15 builds the class onto the stack. This is equivalent to type('A', (), class_dict), where the parameters are exactly the values on the stack at this point.

And finally:

  • 16 stores the freshly created class into the local namespace under the name A.

As we can see, the class declaration is run (12), before the class is assigned to its variable A (16).

Note that the disassembly is different in Python 3, because python as a special builtin class builder function.

And yes, this is far too long an answer ;-).

dhke
  • 15,008
  • 2
  • 39
  • 56
  • now I get it. This just _cannot_ be the same `A` since it's being constructed. So we're both right. your answer is a nice add-on that I had overlooked. – Jean-François Fabre Sep 12 '17 at 21:32
  • @Jean-FrançoisFabre I was not trying to say you were wrong, but the details are a little nasty. Python is a stack machine and distinguishing between "the type object `'A'` on the stack" and a "variable with the name `A` to hold that type object" makes the difference here. – dhke Sep 12 '17 at 21:36