Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using list instead of tuple in module __all__

In many large projects, even in such as Django, and in official Python documentation use list to list the "available from outside" module components in the __init__.py file:

__all__ = [foo, bar, other]

However, the record

__all__ = (foo, bar, other)

it will also work, and in theory it does not give a significant, but an increase in code performance.

Why, then use it to list?

Maybe there is some magic PEP that I don't know about?

like image 745
Protect children of Donbas2014 Avatar asked Feb 08 '21 09:02

Protect children of Donbas2014


People also ask

What is __ all __ In Python module?

It's a list of public objects of that module, as interpreted by import * . It overrides the default of hiding everything that begins with an underscore.

Why use a tuple instead of a list?

Tuples are more memory efficient than the lists. When it comes to the time efficiency, again tuples have a slight advantage over the lists especially when lookup to a value is considered. If you have data which is not meant to be changed in the first place, you should choose tuple data type over lists.

Can you replace tuple in list?

Once a tuple is created, you cannot change its values. Tuples are unchangeable, or immutable as it also is called. But there is a workaround. You can convert the tuple into a list, change the list, and convert the list back into a tuple.

Can we have list as an element of tuple?

We can create a list of tuples i.e. the elements of the tuple can be enclosed in a list and thus will follow the characteristics in a similar manner as of a Python list. Since, Python Tuples utilize less amount of space, creating a list of tuples would be more useful in every aspect.

What is tuples in Python?

Tuples are commonly used as the equivalent of a dictionary without keys to store data. For Example, Above example contains tuples inside list which has a list of movies.

What is the difference between list_NUM and tuple in Python?

Lists are surrounded by square brackets [] and Tuples are surrounded by parenthesis (). Above, we defined a variable called list_num which hold a list of numbers from 1 to 4 .The list is surrounded by brackets []. Also, we defined a variable tup_num; which contains a tuple of number from 1 to 4. The tuple is surrounded by parenthesis ().

What is the difference between tuples and lists in Java?

The key difference between the tuples and lists is that while the tuples are immutable objects the lists are mutable. This means that tuples cannot be changed while the lists can be modified. Tuples are more memory efficient than the lists.

What are the additional functionalities associated with a list over tuple?

We can clearly see that, there are so many additional functionalities associated with a list over a tuple.We can do insert and pop operation, removing and sorting elements in the list with inbuilt functions which is not available in Tuple.


4 Answers

There is no binding reason to use either list or tuple. However, list idiomatically represents a sequence of same kind of items but tuple represents a sequence of different kind of items. This is also encoded by type hints, which have some list[str | int] but positional fields inside tuple[str, int, int].

As such, list more accurately represents "arbitrary sequence of names".

PEP 8 repeatedly makes mention of __all__ being a list:

To better support introspection, modules should explicitly declare the names in their public API using the __all__ attribute. Setting __all__ to an empty list indicates that the module has no public API.

"""This is the example module.

This module does stuff.
"""
...
__all__ = ['a', 'b', 'c']
like image 133
MisterMiyagi Avatar answered Oct 05 '22 13:10

MisterMiyagi


The language reference says:

The public names defined by a module are determined by checking the module’s namespace for a variable named __all__; if defined, it must be a sequence of strings which are names defined or imported by that module.

A sequence is something that supports iteration (for x in __all__) and access using integer indices (__all__[i]). So it can be a list, or a tuple.

like image 20
mkrieger1 Avatar answered Oct 05 '22 14:10

mkrieger1


From a practical standpoint, it's somewhat common to add elements to __all__ semi-dynamically. It happens when you want to expose functions defined deep in the package structure at the top level. This is much easier to do with a list.

A couple of examples of modules that do this are numpy and pyserial. I strongly suspect that Django does this too in places, but am not familiar enough with it to know for sure.

The idiom looks something like this in __init__.py:

__all__ = []  # or might have some initial names

from .subpackage import (name1, name2, name3)

__all__.extend(['name1', 'name2', 'name3']) # or append 1-by-1 or +=

I've even seen a slightly sloppier approach, although arguably more maintainable under certain circumstances, that looks like this:

__all__ = []

from .subpackage import *
from .subpackage import __all__ as _all

__all__.extend(_all)
del _all

Clearly this is greatly simplified by having a mutable __all__. There is no substantial benefit to turning it into a tuple after the fact or "appending" to a tuple using +=.

Another way a mutable __all__ is useful is when your API depends on optional external packages. It's much easier to enable or disable names in a list than a tuple.

Here is an example of a module that enables additional functionality if a library called optional_dependency is installed:

# Core API
__all__ = ['name', 'name2', 'name3']

from .sub1 import name1
from .sub2 import name2, name3

try:
    import optional_dependency
except ImportError:
    # Let it be, it maybe issue a warning
    pass
else:
    from .opt_support import name4, name5

    __all__ += ['name4', 'name5']
like image 31
Mad Physicist Avatar answered Oct 05 '22 15:10

Mad Physicist


Just wanted to document a little error I ran into relevant to this post: note that you need a trailing comma to create a single element tuple. So this:

__all__ = ['foo'] # I am list with one element

Is not the same as this:

__all__ = ('foo') # I am a string

Here's an example of this going wrong. In the second case, if you try to import with the wildcard*:

from mymodule import *

You get the confusing error:

AttributeError: module 'mypackage.mymodule' has no attribute 'f'

What is 'f'?!? Well, it is the first element of __all__, which is pointing to the string 'foo', not the single-element tuple ('foo',).


* Using from x import * is maybe what is more to blame here, as opposed to the tuple vs. list choice. But this still seems to be a relatively common pattern in __init__.py files, and makes me lean towards preferring lists.

like image 22
Tom Avatar answered Oct 05 '22 15:10

Tom