1

I'm learning python class. I'm confused about why c.x prints "dog" (not lemon),even with the diagram. I can understand why a.x prints cat.

Specifically, I have problem understanding why x in the instance C() is points to str "dog " in global frame. And what's the self in the dash-lined box mean and why its parent frame is global frame.

enter image description here

x = "dog"


class A:
    x = "cat"


class B(A):
    x = "ferret"

    def __init__(self):
        self.x = x


class C(B):  # added
    x = "lemon"


c = C()
a = A()


print(f"{c.x = }")  # "dog"
print(f"{a.x = }")  # "cat"
4
  • stackoverflow.com/questions/28119489/… Commented Nov 1, 2021 at 13:03
  • It's subtle but I guess the cause is the line self.x = x; since x is not defined as a param to the constructor, i'd assume it assigns the value of the global var here. Side note, you should be able to fix that by declaring it like self.x = B.x instead. Commented Nov 1, 2021 at 13:06
  • 2
    Because, c is an instance of B (since C inherits from B). Moreover, it inherits B's __init__, in which you specifically assign the global x to the instance attribute x Commented Nov 1, 2021 at 13:07
  • Stack Overflow is not a discussion forum or tutorial resource. You should take questions like this to your instructor or TA. Commented Nov 1, 2021 at 13:07

1 Answer 1

2

Because B's constructor initializes the instance field x to the value of the global x ("dog"), not the class field x.

If you wanted it to initialize an instance field with the class field, you'd do

    def __init__(self):
        self.x = self.__class__.x

(and in fact, the non-sensical-looking

    def __init__(self):
        self.x = self.x

would do the same thing.)


As a related aside, it's good to understand the relation between instance fields and class fields:

class A:
    kind = "cat"


class B(A):
    kind = "ferret"

    def __init__(self):
        self.kind = self.kind


a1 = A()
a2 = A()
print(f"{a1.kind = } {a2.kind = }")
A.kind = "big cat"  # changes the class field, so all As are big from now on:
print(f"{a1.kind = } {a2.kind = }")
a1.kind = "medium cat"  # assigns a new instance field just to a1
print(f"{a1.kind = } {a2.kind = }")

b1 = B()
b2 = B()
print(f"{b1.kind = } {b2.kind = }")
# this has no effect on existing Bs since they copied the value from the class field:
B.kind = "hamster"  
print(f"{b1.kind = } {b2.kind = }")
# However new bs will copy that new hamster value:
b3 = B()
print(f"{b1.kind = } {b2.kind = } {b3.kind = }")

This prints out

a1.kind = 'cat' a2.kind = 'cat'
a1.kind = 'big cat' a2.kind = 'big cat'
a1.kind = 'medium cat' a2.kind = 'big cat'
b1.kind = 'ferret' b2.kind = 'ferret'
b1.kind = 'ferret' b2.kind = 'ferret'
b1.kind = 'ferret' b2.kind = 'ferret' b3.kind = 'hamster'
Sign up to request clarification or add additional context in comments.

8 Comments

Why does the init function creates an enclosing frame to the global frame? would regular function also have the same impact?
__init__ is a function; functions create new scopes when executed. The only role the scope plays here, though, is in providing a namespace where the name self is defined, since you never use a local variable named x. All functions retain a reference to the scope in which the function was defined. __init__ was defined in the global scope, so it keeps a reference to the global scope.
"in the digram, there's a blue line with "enclosing frame" pointing to global frame. what does it mean?" Well, who drew the diagram? Did you try asking that person what it means?
@somniumm it doesn't. There is nothing special about __init__ here. This is just the typical Python scoping rules. Perhaps, importantly, you must understand that class blocks don't create enclosing scopes
@somniumm yes, I thought so. Note, this is why you must refer to other methods using self (or unconventionally, using the class directly).
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.