Python: Dunder name

June 24, 2017
8 min. read

This post is part of the Python Tips series.

You may have seen if __name__ == '__main__': at the bottom of a script. Why is this there and what does it mean?

First, a python file is a module. They can be combined into a directory module and this is what is being done when you see an __init__.py file in the folder.

We will get there in a little while, but first we will cover a little more than just __name__. We have used dir() before, but how do you do this on the current script? To be honest, I’m not exactly sure. However, it is fairly simple to do this with an imported script, so lets start there. I’ll import the file I created when writing the decorators articles.

import decorators

print(dir(decorators))
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add_sugar', 'first', 'hold_function', 'my_decorator', 'my_func', 'my_function', 'outer', 'return_function']

All of the non-dunder output of dir are functions I wrote in the file. The others are generated by Python. Lets use them to look at values of current script and imported script.

I joined these into a dictionary to cover both local script and imported script. I did not use an OrderedDict to keep these in order, because current implementation of the Python dict in CPython is actually an ordered dict, due to some optimizations that occurred recently. I’ll talk about this in a dictionary post in the future.

We will discuss each of these dunders and look at the differences between the local and imported script. I should also note that some of these are dependent on the specific implementation of Python.

builtins

The local version luckily didn’t expand the module for us.

__builtins__ <module 'builtins' (built-in)>

This is good, because the imported script gave enough for both of them:

d.__builtins__ {'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>), '__build_class__': <built-in function __build_class__>, '__import__': <built-in function __import__>, 'abs': <built-in function abs>, 'all': <built-in function all>, 'any': <built-in function any>, 'ascii': <built-in function ascii>, 'bin': <built-in function bin>, 'callable': <built-in function callable>, 'chr': <built-in function chr>, 'compile': <built-in function compile>, 'delattr': <built-in function delattr>, 'dir': <built-in function dir>, 'divmod': <built-in function divmod>, 'eval': <built-in function eval>, 'exec': <built-in function exec>, 'format': <built-in function format>, 'getattr': <built-in function getattr>, 'globals': <built-in function globals>, 'hasattr': <built-in function hasattr>, 'hash': <built-in function hash>, 'hex': <built-in function hex>, 'id': <built-in function id>, 'input': <built-in function input>, 'isinstance': <built-in function isinstance>, 'issubclass': <built-in function issubclass>, 'iter': <built-in function iter>, 'len': <built-in function len>, 'locals': <built-in function locals>, 'max': <built-in function max>, 'min': <built-in function min>, 'next': <built-in function next>, 'oct': <built-in function oct>, 'ord': <built-in function ord>, 'pow': <built-in function pow>, 'print': <built-in function print>, 'repr': <built-in function repr>, 'round': <built-in function round>, 'setattr': <built-in function setattr>, 'sorted': <built-in function sorted>, 'sum': <built-in function sum>, 'vars': <built-in function vars>, 'None': None, 'Ellipsis': Ellipsis, 'NotImplemented': NotImplemented, 'False': False, 'True': True, 'bool': <class 'bool'>, 'memoryview': <class 'memoryview'>, 'bytearray': <class 'bytearray'>, 'bytes': <class 'bytes'>, 'classmethod': <class 'classmethod'>, 'complex': <class 'complex'>, 'dict': <class 'dict'>, 'enumerate': <class 'enumerate'>, 'filter': <class 'filter'>, 'float': <class 'float'>, 'frozenset': <class 'frozenset'>, 'property': <class 'property'>, 'int': <class 'int'>, 'list': <class 'list'>, 'map': <class 'map'>, 'object': <class 'object'>, 'range': <class 'range'>, 'reversed': <class 'reversed'>, 'set': <class 'set'>, 'slice': <class 'slice'>, 'staticmethod': <class 'staticmethod'>, 'str': <class 'str'>, 'super': <class 'super'>, 'tuple': <class 'tuple'>, 'type': <class 'type'>, 'zip': <class 'zip'>, '__debug__': True, 'BaseException': <class 'BaseException'>, 'Exception': <class 'Exception'>, 'TypeError': <class 'TypeError'>, 'StopAsyncIteration': <class 'StopAsyncIteration'>, 'StopIteration': <class 'StopIteration'>, 'GeneratorExit': <class 'GeneratorExit'>, 'SystemExit': <class 'SystemExit'>, 'KeyboardInterrupt': <class 'KeyboardInterrupt'>, 'ImportError': <class 'ImportError'>, 'ModuleNotFoundError': <class 'ModuleNotFoundError'>, 'OSError': <class 'OSError'>, 'EnvironmentError': <class 'OSError'>, 'IOError': <class 'OSError'>, 'WindowsError': <class 'OSError'>, 'EOFError': <class 'EOFError'>, 'RuntimeError': <class 'RuntimeError'>, 'RecursionError': <class 'RecursionError'>, 'NotImplementedError': <class 'NotImplementedError'>, 'NameError': <class 'NameError'>, 'UnboundLocalError': <class 'UnboundLocalError'>, 'AttributeError': <class 'AttributeError'>, 'SyntaxError': <class 'SyntaxError'>, 'IndentationError': <class 'IndentationError'>, 'TabError': <class 'TabError'>, 'LookupError': <class 'LookupError'>, 'IndexError': <class 'IndexError'>, 'KeyError': <class 'KeyError'>, 'ValueError': <class 'ValueError'>, 'UnicodeError': <class 'UnicodeError'>, 'UnicodeEncodeError': <class 'UnicodeEncodeError'>, 'UnicodeDecodeError': <class 'UnicodeDecodeError'>, 'UnicodeTranslateError': <class 'UnicodeTranslateError'>, 'AssertionError': <class 'AssertionError'>, 'ArithmeticError': <class 'ArithmeticError'>, 'FloatingPointError': <class 'FloatingPointError'>, 'OverflowError': <class 'OverflowError'>, 'ZeroDivisionError': <class 'ZeroDivisionError'>, 'SystemError': <class 'SystemError'>, 'ReferenceError': <class 'ReferenceError'>, 'BufferError': <class 'BufferError'>, 'MemoryError': <class 'MemoryError'>, 'Warning': <class 'Warning'>, 'UserWarning': <class 'UserWarning'>, 'DeprecationWarning': <class 'DeprecationWarning'>, 'PendingDeprecationWarning': <class 'PendingDeprecationWarning'>, 'SyntaxWarning': <class 'SyntaxWarning'>, 'RuntimeWarning': <class 'RuntimeWarning'>, 'FutureWarning': <class 'FutureWarning'>, 'ImportWarning': <class 'ImportWarning'>, 'UnicodeWarning': <class 'UnicodeWarning'>, 'BytesWarning': <class 'BytesWarning'>, 'ResourceWarning': <class 'ResourceWarning'>, 'ConnectionError': <class 'ConnectionError'>, 'BlockingIOError': <class 'BlockingIOError'>, 'BrokenPipeError': <class 'BrokenPipeError'>, 'ChildProcessError': <class 'ChildProcessError'>, 'ConnectionAbortedError': <class 'ConnectionAbortedError'>, 'ConnectionRefusedError': <class 'ConnectionRefusedError'>, 'ConnectionResetError': <class 'ConnectionResetError'>, 'FileExistsError': <class 'FileExistsError'>, 'FileNotFoundError': <class 'FileNotFoundError'>, 'IsADirectoryError': <class 'IsADirectoryError'>, 'NotADirectoryError': <class 'NotADirectoryError'>, 'InterruptedError': <class 'InterruptedError'>, 'PermissionError': <class 'PermissionError'>, 'ProcessLookupError': <class 'ProcessLookupError'>, 'TimeoutError': <class 'TimeoutError'>, 'open': <built-in function open>, 'quit': Use quit() or Ctrl-Z plus Return to exit, 'exit': Use exit() or Ctrl-Z plus Return to exit, 'copyright': Copyright (c) 2001-2017 Python Software Foundation.
All Rights Reserved.

Copyright (c) 2000 BeOpen.com.
All Rights Reserved.

Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.

Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved., 'credits':     Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
    for supporting Python development.  See www.python.org for more information., 'license': Type license() to see the full license text, 'help': Type help() for interactive help, or help(object) for help about object.}

Dictionaries are used all over Python and what we see here are all the elements that are built in to Python and can be called directly. This includes functions, exceptions, classes, etc. Take a look through and see False, None, all, any, and even what we used to get this: dir.

One thing I didn’t expect are the dual strings, like quit or exit. Open up a Python session and type ‘exit’ and you will receive the text after it. So the same would work with copyright.

Take a look through the functions and classes. Which ones have you not heard of before? Use your web search and learn about them. (Yes, I just assigned homework. But there will be no test.)

cached

Here are the results from both local and imported:

__cached__ None
d.__cached__ C:\Users\micro\repositories\scratchpad\__pycache__\decorators.cpython-36.pyc

If you look in one of your Python folders, chances are you will see .pyc files. These are compiled python bytecode. This speeds up running by caching this compilation effort. For the imported module we have run before, we have the path to the .pyc file.

doc

__doc__ None
d.__doc__ None

Ok, this is kind of sad. Maybe I should make some module documentation. I’ll put this at the top of my local script:

"""
 This is triple quoted documentation, right at the top of the script
"""

What happens when we look at __doc__ again?

__doc__
This is a triple quoted documentation right in the high level of the module

d.__doc__ None

Viola! (Wait, that is a stringed instrument.)

VoilĂ ! A module documentation string.

file

This gives you the filename of the script the module (or local) is running from. Pretty simple.

__file__ C:\Users\micro\repositories\scratchpad\dunder_name.py
d.__file__ C:\Users\micro\repositories\scratchpad\decorators.py

loader

This stores the object used for loading the source file.

__loader__ <_frozen_importlib_external.SourceFileLoader object at 0x04EC8370>
d.__loader__ <_frozen_importlib_external.SourceFileLoader object at 0x050F26F0>

If you want to learn how source files are loaded and parsed, take a look at the import system documentation.

name

Finally! This is all I wanted to know, Joe. Why did you take so long to get here?

Well if I just wrote this little section, it would be closer to a tweet than a post. What does __name__ contain?

__name__ __main__
d__name__ decorators

The imported script has the name that we imported decorators. The local script has the name __main__. So when we are checking if __name__ == '__main__': we are seeing if the script is being run by starting this script directly.

Code can be put in here that will run if the script is run directly, but will not run if it is imported. I used to use this to write simple testing code. However, this ended with writing proper test pre-fixed or post-fixed function names that can be run automatically by pytest (if putting testing code in the same file.)

This is still useful for setup objects or other pieces that are needed for a library to be run on its own.

package

This is certainly not interesting. We have None for the local package, as we are running the script so it isn’t part of a package. The imported single script isn’t None but is an empty string.

__package__ None
d.__package__

Lets see what happens when we create a package. I made a joe folder that only contains an empty __init__.py file. This is the simplest package you can create. It is importable and does absolutely nothing. However, when you import it and execute print('j.__package__', joe.__package__), you get the following:

j.__package__ joe

This tells us what the package is named that the code comes from. I’m not sure if the value is only None for the local script.

spec

This is the module specification that is used between the finder and loader when importing and running python modules. This is a combination of some we have seen before. origin is the place where the module is imported. This will be a python file or may be ‘builtin’ for built in modules.

__spec__ None
d.__spec__ ModuleSpec(name='decorators', loader=<_frozen_importlib_external.SourceFileLoader object at 0x050B2730>, origin='C:\\Users\\micro\\repositories\\scratchpad\\decorators.py')

Summary

For those of you that just skipped to the end, __name__ == '__main__' is true if the script was run directly. For those that wanted to learn more than just that, hopefully this was interesting.

I was going to just write a small post on this, but decided to look at everything and even learned a little myself doing so. That is one of my measures for a good day. Until next post.


Part 8 of 8 in the Python Tips series.

Series Start | Python: Iterators and Generators

comments powered by Disqus