== What the ((co_names)) attribute on Python code objects is As a trap for the unwary, Python code objects have both a ((co_names)) and a ((co_varnames)) attribute. Since I just confused myself about which was what [[the other day AbusingFrames]], here is what the ((co_names)) one is. Put simply, ((co_names)) is a tuple of names of globals and attributes that are used by the function's code. For example, if you have '_a = self.bar()_' in the function, the '_bar_' will show up in ((co_names)), as will the '_foo_' from '_a = foo()_'. (Perhaps I should call these 'identifiers' instead of 'names'. In Ruby and Lisp and probably elsewhere these are called symbols.) Ultimately this is part of how the CPython bytecode interpreter is implemented. When the bytecode interpreter refers to anything but a [[local variable WhyLocalVarsAreFast]], it has to do an attribute lookup with the name to get the actual object involved. Rather than put the name that's being looked up directly in the bytecode instructions, CPython puts all the names into a table and has the instructions refer to table slots, so the ((LOAD_GLOBAL)) instruction says 'look up name 3' instead of 'look up "somevar"'. And ((co_names)) is that table (or at least a representation of that table). Each name only appears once in ((co_names)), no matter how many times it's used in your function and no matter if it's used in different contexts; if you have both '_obj.foo_' and '_foo()_' in your code, there will only be one "foo" in ((co_names)), even though one use of the name is for an object attribute and one is for a global. As far as I can tell from reading CPython source, names are always interned strings and so are globally unique. (The ((co_names)) table slot numbers are of course a per-function thing; slot 0 in different functions will refer to completely different names.)