Difference between from collections import Container and from collections.abc import Container

与世无争的帅哥 提交于 2019-12-24 11:49:12

问题


We can import Container in two ways:

  1. from collections import Container
  2. from collections.abc import Container

help function for both Container returns the same documentation.

help(collections.Container):

Help on class Container in module collections.abc:

class Container(builtins.object)
 |  Methods defined here:
 |  
 |  __contains__(self, x)
 |  
 |  ----------------------------------------------------------------------
 |  Class methods defined here:
 |  
 |  __subclasshook__(C) from abc.ABCMeta
 |      Abstract classes can override this to customize issubclass().
 |      
 |      This is invoked early on by abc.ABCMeta.__subclasscheck__().
 |      It should return True, False or NotImplemented.  If it returns
 |      NotImplemented, the normal algorithm is used.  Otherwise, it
 |      overrides the normal algorithm (and the outcome is cached).
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __abstractmethods__ = frozenset({'__contains__'})

help(collections.abc.Container):

Help on class Container in module collections.abc:

class Container(builtins.object)
 |  Methods defined here:
 |  
 |  __contains__(self, x)
 |  
 |  ----------------------------------------------------------------------
 |  Class methods defined here:
 |  
 |  __subclasshook__(C) from abc.ABCMeta
 |      Abstract classes can override this to customize issubclass().
 |      
 |      This is invoked early on by abc.ABCMeta.__subclasscheck__().
 |      It should return True, False or NotImplemented.  If it returns
 |      NotImplemented, the normal algorithm is used.  Otherwise, it
 |      overrides the normal algorithm (and the outcome is cached).
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __abstractmethods__ = frozenset({'__contains__'})

What is the difference between these two imports? Why are we allowed to do both?

Update

Got deprecation warning while importing Container from collections (Python 3.7.3).

From Python 3.8 it cannot be imported directly from collections.

>>> from collections import Container

main:1: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working


回答1:


From the Python 3 documentation for the collections module:

Changed in version 3.3: Moved Collections Abstract Base Classes to the collections.abc module. For backwards compatibility, they continue to be visible in this module through Python 3.7. Subsequently, they will be removed entirely.

These "Collections Abstract Base Classes" currently include AsyncGenerator, AsyncIterable, AsyncIterator, Awaitable, Bytestring, Callable, Collection, Container, Coroutine, Generator, Hashable, ItemsView, Iterable, Iterator, KeysView, Mapping, MappingView, MutableMapping, MutableSequence, MutableSet, Reversible, Sequence, Set, Sized, ValuesView.

In Python 3.8 importing them from collections will stop working. In Python 3.3 to 3.7, they can be imported from collections or from collections.abc (it gives the exact same classes). In Python 3.7, importing them from collections prints a deprecation warning, since Python 3.8 is getting near.

In Python 2 they can only be imported from 'collections', not from 'collections.abc'.

One simple way to deal with this is a try/except block:

try:  # works in Python >= 3.3
    from collections.abc import Sequence
except ImportError:  # Python 2, Python <= 3.2
    from collections import Sequence

Another commonly used workaround is to conditionally import from collections or collections.abc depending on the Python version being used.

For instance, have a PY2 boolean and do:

if PY2:
    from collections import Sequence
else:
    from collections.abc import Sequence

This boolean is usually obtained either using six:

from six import PY2

or using sys.version_info:

import sys
PY2 = int(sys.version_info[0]) == 2

If we anticipate that Python 4 is likely to work like Python 3.3+ in this respect, special-casing Python 2 seems more future-proof than special-casing Python 3, which could be done as follows:

if PY3:
    from collections.abc import Sequence
else:
    from collections import Sequence

where the PY3 boolean can be obtained either using six:

from six import PY3

or using sys.version_info:

import sys
PY3 = int(sys.version_info[0]) == 3

The try/except approach above seems even more robust though (e.g. it works with Python 3.2 with no extra effort).



来源:https://stackoverflow.com/questions/55882715/difference-between-from-collections-import-container-and-from-collections-abc-im

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!