Suppose you had code like this:
_READERS = None
_WRITERS = None
def Init(num_readers, reader_params, num_writers, writer_params, ...args...):
...logic...
_READERS = new ReaderPool(num_readers, reader_params)
_WRITERS = new WriterPool(num_writers, writer_params)
...more logic...
class Doer:
def __init__(...args...):
...
def Read(self, ...args...):
c = _READERS.get()
try:
...work with conn
finally:
_READERS.put(c)
def Writer(...):
...similar to Read()...
To me, this is a bad pattern to follow, some cons:
Doers can be created without its preconditions being satisfied
The code isn't easily testable because ConnPool can't be directly mocked out.
Init has to be called right the first time. If its changed so it can be called multiple times, extra logic has to be added to check if variables are already defined, and lots of NULL values have to be passed around to skip re-initializing.
In the event of threads, the above becomes more complicated by adding locking
Globals aren't being used to communicate state (which isn't strictly bad, but a code smell)
On the other hand, some pros:
its very convenient to call Init(5, "user/pass", 2, "user/pass")
It simple and "clean"
Personally, I think the cons outweigh the pros, that is, testability and assured preconditions outweigh simplicity and convenience.