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 thex
attribute__getattr__
is only being called when the accessed attribute does not exist on the instanceSubclasses 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