What is the difference between, e.g. the int and code types, that justifies having a built-in name for one but not the other. To me this implies an oversight in the language specification, but if so it has been overlooked for a very long time.
The *types* package defines *CodeType* but is there any reason why the actual type name should not be used, as for example:
code = compile('None', '', 'eval').__class__
__builtins__.__dict__['code'] = compile('None', '', 'eval').__class__
which places the definition of *code* in the same place as built-in types such as *int* and *str*. If I knew a way to enumerate every currently defined class then I could write a procedure to automatically create definitions for all those not currently defined.
One reason that I consider the current situation unsatisfactory is that it defeats introspection. Consider:
def f(x, y, u=None):
if u is None:
return x + y
elif isinstance(u, types.FunctionType):
return u(x, y)
print('Invalid type: ' + u.__class__.__name__)
If *u* is a function written in Python then it will work but if it is a built-in function such as max then it will report invalid type *builtin_function_or_method*. However, I cannot use that name in an *isinstance*, as I could if it was *int* or *str* but must instead hunt through *types* for the definition there.
Restating my question, it seems that we have here an unnecessary hole untidily patched and I am asking:
1. Why does the hole exist?
2. Why, when doing so seems so simple, has it not been fixed almost invisibly?
3. What adverse consequences might ensue from adding the actual names of these classes either to builtins or globals?
4. Can anyone tell me a way to enumerate all classes?
> 1. Why does the hole exist?
`__builtins__` (the `builtins` module) is just a namespace for things you can use without directly importing. It doesn't actually _own_ anything; `int` isn't _actually_ part of the `builtins` module, it's part of Python that happens to be exported by the `builtins` module for convenience.
> Namespaces are one honking great idea -- let's do more of those!
>   — Tim Peters, _The Zen of Python_ (`import this`)
Being in the `builtins` module doesn't make something more built in; `math.sqrt` is the same kind of thing as `len` or `ord`, and is just as much part of the CPython interpreter. However, there's a useful distinction to be made between them; `len` is a fundamental feature you _need_ for writing pretty much all Python code, but `math.sin`? Not so much.
Having `len` in `builtins` and `sin` in `math` is useful.
> 2. Why, when doing so seems so simple, has it not been fixed almost invisibly?
> 3. What adverse consequences might ensue from adding the actual names of these classes either to builtins or globals?
I don't know the mind of the Python developers, but there are a few reasons that spring to mind:
* Python is designed to be easy for novices to pick up. If `function`, `code` and `member_descriptor` were in the `builtins` (and hence autocompleted in IDLE, and so on), people new to the language would get confused and frustrated. You can't instantiate a `member_descriptor`, and it's not even a particularly _meaningful concept_ unless you're digging into CPython implementation details.
* Including implementation details in the `builtins` would make it a lot harder to change how Python works without breaking stuff, and effectively means that no Python implementation can work differently to CPython. No PyPy, no Jython, no IronPython.
* Providing a lot of similar things encourages developers to accidentally break ducktyping (e.g. only allowing user-defined functions to be passed as callbacks, stopping you from using `print`).
I wouldn't call your proposal a “fix”.
> 4. Can anyone tell me a way to enumerate all classes?
Note that this list will grow longer as you `import` more modules; this only enumerates the types that are currently loaded into the Python interpreter. As such, its behaviour will be different in IDLE than the `python3 -i`; IDLE's runtime `import`s several modules before it shows you the `>>>`. (Of course, `import`ing those not-yet-loaded modules in `python3 -i` will remove the difference.)