The sorted(__all__ + deprecated_names)
version assumes that __all__
contains all your module’s public names, except for deprecated names. If
you haven’t defined __all__
, or if it is out of date, then it will
miss some things.
The [*globals().keys(), *deprecated_names]
version grabs everything
from the module namespace, whether you intend them to be listed or not,
including global variables.
What’s the difference? Suppose your module looks like this:
__all__ = ['spam', 'eggs', 'Cheese', 'foo']
import sys
# Public names
spam = 'spam, eggs, spam, spam and spam on toast'
def eggs():
global storage
storage = 1
class Cheese:
pass
# Private names
def _private():
pass
# Incidental variables
for i in range(3):
print('Hello world')
# Deprecated names
deprecated = ['bar',]
# Magic
def __dir__():
...
then the __all__
version will give you a dir() of:
spam, eggs, Cheese, foo, bar
while the version with globals will give you a dir() of:
__all__, sys, spam, eggs, Cheese, _private, i, deprecated, bar, __dir__
plus some additional internal details of modules:
__builtins__, __cached__, __doc__, __file__, __loader__, __name__,
__package__, __spec__
(intentionally not sorting the output but leaving it in input order).
Notice that the global variable “storage” doesn’t show up. That’s
because I haven’t run eggs()
. Once I run eggs()
, the second version
of dir will change to include it.
In the first case, because your __all__
wrongly includes the obsolete
name “foo”, it still shows up in dir(); in the second case you get a
bunch of stuff you probably don’t want to see, like imports and dunders.
Personally, I strongly dislike the default output of dir()
and I run a
monkey-patched version which strips out all those dunder names unless I
explicitly ask for them. If I were to take the time and effort to define
a custom __dir__
for my module, the last thing I would want to do is
fill it with module dunders and private names.
But it’s your module, and you get to choose whatever behaviour makes the
most sense to you.