No, these are two different things.
In Python, everything is an object. Classes are objects, functions are objects and instances are objects. Since everything is an object, everything behaves in a similar way. In your case, you create a class instance (== an object with the type "Class") with the name "pytest". That object has two attributes: i
and fuc
. i
is an instance of "Integer" or "Number", fuc
is an instance of "Function".
When you use "pytest.j", you tell python "look up the object pytest
and when you have it, look up i
". "pytest" is a class instance but that doesn't matter.
When you create an instance of "pytest" (== an object with the type "pytest"), then you have an object which has "defaults". In your case, a
is an instance of pytest
which means that anything that can't be found in a
will be searched in pytest
, next.
So a.j
means: "Look in a
. When it's not there, also look in pytest
". But j
doesn't exist and Python now has to give you a meaningful error message. It could say "class pytest has no attribute 'j'". This would be correct but meaningless: You would have to figure out yourself that you tried to access j
via a
. It would be confusing. Guido won't have that.
Therefore, python uses a different error message. Since it does not always have the name of the instance (a
), the designers decided to use the type instead, so you get "pytest instance...".