Reputation: 2061
I was working with Queues
and everything seemed to go fine. However, I found a situation that makes an unexpected AttributeError
exception to arise.
Let's create the simplest code to create a Queue
. Don't worry about it's usage, it all happens in the same side, we don't have to worry about the other side that will communicate with it.
import multiprocessing
q = multiprocessing.Queue()
print(type(q))
This code will produce as output:
<class 'multiprocessing.queues.Queue'>
But what happens if I try to identify if the object is an instance of this class?
isinstance(q, multiprocessing.queues.Queue)
Obviously it returns True
.
Finally, my question arose due to that, if I don't create a Queue
instance before that checking, the module multiprocessing.queues
where Queue
is defined, is not loaded! (Warning, maybe loaded is not the appropriate word here since we are talking about modules. I'm not an expert on it) Let's check it:
import multiprocessing
a = dict()
isinstance(a, multiprocessing.queues.Queue)
Where surprisingly we get the failure:
1 isinstance(a, multiprocessing.queues.Queue)
AttributeError: module 'multiprocessing' has no attribute 'queues'
Why is this happening? Is this the intended behaviour or is this kind of a bug?
For me, this represents a problem because in case I want to check if an object is a Queue
and it's not, if no other queue has been instanced my check could end in an undesired AttributeError
.
Finally, I would like to add a quick picture with some checks that I did reproducing this, plus adding some checks for the queues
module inside the multiprocessing.__all__
.
These checks have been performed in Python 3.9.1. It is worth noting that, when multiprocessing.queues
was already recognized and therefore the checks didn't throw the AttributeError
, queues
was present in multiprocessing.__dict__
. In the other cases, it is not.
This may seem natural to the expert eye, since __dict__
is (AFAIK) a list of attributes, but I wanted to point it out since as I said I'm not an expert and I don't know the actual implications of it.
PS. I've tagged the question as Python 3.8
since it has been tested in both 3.8
and 3.9
with same results, and in case there are differences I would like the answer for 3.8
. Those differences are likely to appear due to multiprocessing library changes as the docs reflects in several footnotes at the end of the functions.
PS.2. I've been referring to multiprocessing.queues
as a module due to the output of multiprocessing.__dict__
. Again I'm not an expert on the matter:
'queues': <module 'multiprocessing.queues' from 'C:\\Program Files\\Python39\\lib\\multiprocessing\\queues.py'>
Upvotes: 1
Views: 958
Reputation: 52149
This is intended behaviour. If you want to access a submodule, you should import it:
import multiprocessing.queues
a = dict()
isinstance(a, multiprocessing.queues.Queue)
Importing submodules adds them as attributes to their parent module – note how multiprocessing.queues.Queue
is a nested attribute lookup of Queue
on queue
after queue
on multiprocessing
. An explicit import ensures the submodule will be loaded before accessing it.
The import system: 5.4.2. Submodules
When a submodule is loaded using any mechanism (e.g.
importlib
APIs, theimport
orimport-from
statements, or built-in__import__()
) a binding is placed in the parent module’s namespace to the submodule object. For example, if packagespam
has a submodulefoo
, after importingspam.foo
,spam
will have an attributefoo
which is bound to the submodule.
This means one can get away skipping an explicit export when another action imports the submodule as a side effect. As with all side effects, one has to actually perform the triggering action in order to rely on the side effect.
Since imports are idempotent (repeating them has no ill effect), one should import any module one expects to access instead of relying on side effects of other code.
Upvotes: 3