Yep. The GIL means that individual Python bytecode operations are atomic, and so by extension single Python statements that translate to individual bytecode instructions can be treated as atomic (if you don't care about portability to alternate Python implementations that lack a GIL). But complicated Python statements that translate into multiple bytecode instructions (like dict.setdefault) can't. And, of course, neither can sequences of multiple statements. So you still need locks for most cases where you mutate objects that are shared across threads.