Why pycharm can not find this attributes and methods reference?
class A:
def __init__(self):
print("A.__init__")
self.method_need_B()
self.method_need_C()
print(self.attr_need_B)
print(self.attr_need_C)
class B(A):
def __init__(self):
print("B.__init__")
self.attr_need_B = "I am 'attribute' and A need me, but live in B"
A.__init__(self)
def method_need_B(self):
print("I am 'method' and A need me, but live in B")
class C(B):
def __init__(self):
print("C.__init__")
self.attr_need_C = "I am 'attribute' and A need me, but live in C"
B.__init__(self)
def method_need_C(self):
print("I am 'method' and A need me, but live in C")
c = C()
this code on picture, i am not undestand why pycharm not visible this attrs and methods outputs without errors:
C.__init__
B.__init__
A.__init__
I am 'method' and A need me, but live in B
I am 'method' and A need me, but live in C
I am 'attribute' and A need me, but live in B
I am 'attribute' and A need me, but live in C
CodePudding user response:
Because they do not exist inside A class.
Not only pycharm cannot find references. The python interpreter would not be able to find it as well. If you will try such instruction:
a = A()
you will get error.
Why your code sample works though?
You are calling inside C.__init__
such instruction: B.__init__(self)
, the self
object does have the method_need_C
, and going down, you add second method the same way in the B class. So the self
in the A class does have those two methods.
CodePudding user response:
The script works without errors but it is very situational: it only works for the case displayed on your question.
What if, for instance, you call c = B()
?
That's the output:
B.__init__
A.__init__
I am 'method' and A need me, but live in B
Traceback (most recent call last):
File "C:\Users\xxx\Documents\test.py", line 29, in <module>
c = B()
File "C:\Users\xxx\Documents\test.py", line 15, in __init__
A.__init__(self)
File "C:\Users\xxx\Documents\test.py", line 6, in __init__
self.method_need_C()
AttributeError: 'B' object has no attribute 'method_need_C'. Did you mean: 'method_need_B'?
Why it didn't worked on c = B()
, but it worked on c = C()
?
When you assign c
an instance of class C
, the class constructor calls B.__init__(self)
, which inside B constructor, it calls A.__init__(self)
.
So, c
is an object with A, B and C methods and properties. As all of them have different function and variable names, they do not get overwritten once you inherit each parent's data.
Now, why does c = B()
does not work?
It's because that on class A
, you're trying to access class C
methods and properties, and class C
was never initialized inside class B
instance. So basically, as C.__init__(self)
is never called on this instance, then self.method_need_C()
and self.attr_need_C
are not allocated.
Now, you asked, why on c = C()
these methods are visible? It's because that on python, self
refers to the instance of the class that you created, which in this case is C
.
Sure, the script being executed is inside class A
, but have you tried to print self
to see what is its type inside class A
's constructor?
# By modifying your script, we now print self to see its type:
class A:
def __init__(self):
print("A.__init__: ", self)
self.method_need_B()
self.method_need_C()
print(self.attr_need_B)
print(self.attr_need_C)
This is the output:
C.__init__
B.__init__
A.__init__: <__main__.C object at 0x0000029641A27B80>
I am 'method' and A need me, but live in B
I am 'method' and A need me, but live in C
I am 'attribute' and A need me, but live in B
I am 'attribute' and A need me, but live in C
As self refers to a class C
instance, even inside class A
it contains class B
and class C
methods and properties. So they are visible as long as you use self
on the script.
Again, this only works because you're creating a class C
instance. Instantiating another class type will get you lots of errors.
One thing you can do, if you really need to call class C
and class B
methods inside class A
, is to use isinstance()
function, as displayed below:
class A:
def __init__(self):
print("A.__init__: ", self)
if (isinstance(self, B)):
self.method_need_B()
if (isinstance(self, C)):
self.method_need_C()
if (isinstance(self, B)):
print(self.attr_need_B)
if (isinstance(self, C)):
print(self.attr_need_C)
class B(A):
def __init__(self):
print("B.__init__")
self.attr_need_B = "I am 'attribute' and A need me, but live in B"
A.__init__(self)
def method_need_B(self):
print("I am 'method' and A need me, but live in B")
class C(B):
def __init__(self):
print("C.__init__")
self.attr_need_C = "I am 'attribute' and A need me, but live in C"
B.__init__(self)
def method_need_C(self):
print("I am 'method' and A need me, but live in C")
c = B()
The output now does not give you any errors:
B.__init__
A.__init__: <__main__.B object at 0x000001F82AA57F70>
I am 'method' and A need me, but live in B
I am 'attribute' and A need me, but live in B
That's because you're testing to see if self
is an instance of class C
or class B
and prevent accessing methods and properties that do not exist, in case they are not instances of those classes.