0

In simple case I can call teacher in loh function:

class Student:
   teacher = 'Mrs. Jones'

   def __init__(self, name):
       self.name = name
    
def loh():
  print(Student.teacher)

loh()

### Mrs. Jones

But, If I try to do that next way, I got mistake "not defined". Why?

class Student:
  class UnderStudent:
    teacher = 'Mrs. Jones'
  def f():
    print(UnderStudent.teacher)
  f()
  • 2
    Does this answer your question? [Short description of the scoping rules?](https://stackoverflow.com/questions/291978/short-description-of-the-scoping-rules) – MisterMiyagi Feb 22 '22 at 15:35
  • ...and notice that "inside nested classes" is **not** listed as one of the scopes searched in the accepted answer. Very little is gained from nesting classes in Python which is why it's seldom used. I do it anyway sometimes, just to indicate that nested class isn't used anywhere else (although it *is* still accessible outside the class). – martineau Feb 22 '22 at 16:31

2 Answers2

4

f tries to lookup UnderStudent in the global scope, but the name isn't defined there; it's only defined in the namespace of the class statement, which turns the name into a class attribute.

class Student:
    class UnderStudent:
        teacher = 'Mrs.Jones'
    def f(self):
        print(self.UnderStudent.teacher)
        # or print(Student.UnderStudent.teacher)

Nested classes are rare in Python, as there is no restriction on having multiple top-level classes in the same file as in some other languages.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • 1
    @SharimIqbal: `f` is a method of `Student` objects; you need to create a `Student` to call `f` on first. – ShadowRanger Feb 22 '22 at 15:39
  • 1
    Because I added `self` as the missing parameter to your instance method. Why would you want to call `f` from inside the `class` statement anyway? – chepner Feb 22 '22 at 15:39
4

You can't do it that way for the same reason you can't just say teacher without qualifying it with self or Student (Student.teacher is a class attribute of Student in your first example; in your second example, Student.UnderStudent is a class attribute of Student that just happens to be a class itself). UnderStudent doesn't exist at global scope, it only exists within the scope of Student, just like teacher did.

You also have to invoke f after Student is fully defined, because until you exit the scope of Student's definition, Student doesn't exist in global scope (so you can't use it to find UnderStudent). To fix, you could do:

class Student:
  class UnderStudent:
    teacher = 'Mrs. Jones'
  def f(self):
    print(Student.UnderStudent.teacher)
    # or
    print(self.UnderStudent.teacher)

Student().f()
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • I feel like this misses an important point of *why* ``UnderStudent`` refers to the global scope and not the containing scope – like it would do if it were ``def Student...`` instead of ``class Student...``. – MisterMiyagi Feb 22 '22 at 15:39
  • @MisterMiyagi: `UnderStudent` doesn't refer to the global scope, so I don't know what's confusing you. The problem is that, like *anything* defined inside a class, it exists within that class's scope. `UnderStudent` is an attribute of `Student`, just like any other method or class attribute of `Student` (the OP seems to understand the concept of class attributes after all; `UnderStudent` is neither more nor less than a class attribute that just happened to be defined as a nested class rather than through simple assignment). – ShadowRanger Feb 22 '22 at 15:41
  • The bare reference ``UnderStudent`` (as e.g. in the OP's ``print(UnderStudent.teacher)``) *does* refer to global scope. That's usually not how nested name resolution works, which allows accessing the containing scope in any other case. I'm not *confused* by the answer (I know how this works) but think this is an important point since people usually don't get that there are different kinds of scopes when only presented with one. – MisterMiyagi Feb 22 '22 at 15:58