Home > Net >  Why defined attribute value is not printing here?
Why defined attribute value is not printing here?

Time:12-14

I've the following code:

class PageElement:
    def __init__(self, a, b):
        self.a = a
        self.x = b

    @property
    def prop(self):
        print(f"{self.x}")
        return 1

class Tag(PageElement):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __getattr__(self, x):
        self.x = x
        print("Tag.getattr was invoked")

t = Tag(3, 4)
print(t.prop)

it outputs

Tag.getattr was invoked
None
1

I checked t.__dict__ to be {'a': 3, 'b': 4, 'x': 'x'}, but still the value of t.x (='x') is not printed when print(f"{self.x}") is executed in the source code. I can't understand how on Earth f"{self.x}" is getting evaluated to None. Kindly help.

CodePudding user response:

You need to call init of PageElement (SuperClass), when you try to print

print(f"{self.x}")

Your self.x is undefined because you don't call constructor of SuperClass (PageElement). You can add this call to SubClass (Tag)

class PageElement:
    def __init__(self, a, b):
        self.a = a
        self.x = b

    @property
    def prop(self):
        print(f"{self.x}")
        return 1


class Tag (PageElement):
    def __init__(self, a, b):
        self.a = a
        self.b = b
        super().__init__(self.a, self.b)

    def __getattr__(self, x):
        self.x = x
        print("Tag.getattr was invoked")


t = Tag(3, 4)
print(t.prop)

CodePudding user response:

I think that several misconceptions about Python's data model are involved here.

  • __getattr__ should return an attribute. In this code it assigns the named of the accessed attribute (the argument that it receives) to the x attribute

  • __getattr__ is only being called when the accessed attribute does not exist on the instance

  • Subclasses should almost always explicitly call the parent's class __init__ method.

Due the first 2 points, any subsequent calls to Tag.prop will in fact output x:

t = Tag(3, 4)
print(t.prop)
print(t.prop)

outputs

Tag.getattr was invoked
None
1
x
1

This is because on the first call to t.prop, x does not exist on t (because PageElement.__init__ was never called). This triggers the call to Tag.__getattr__ that assigns x but never returns it. This is where the None comes from.

On the second call to t.prop, x does exist so __getattr__ is not called (so None is not returned from any where). Because x does exist now, prop can print it and it prints x.

All of this is unnecessarily over-engineered , and instead you should just be calling PageElement.__init__ from Tag.__init__:

class PageElement:
    def __init__(self, a, b):
        self.a = a
        self.x = b

    @property
    def prop(self):
        print(f"{self.x}")
        return 1

class Tag(PageElement):
    def __init__(self, a, b):
        super().__init__(a, b)

t = Tag(3, 4)
print(t.prop)

This outputs

4
1
  • Related