Что делает isinstance со словарем и abc.Mapping из коллекций?

Код, который я использую:

>>> from collections import abc
>>> mydict = {'test_key': 'test_value'}
>>> isinstance(mydict, abc.Mapping)
True

Я понимаю, что делает isinstance, но не уверен, что делает abc.Mapping из collections?

Кажется, строка isinstance(mydict, abc.Mapping) используется для проверки того, что mydict словарь?

Разве не было бы проще сделать isinstance(mydict, dict)?

Я немного поискал и нашел соответствующие комментарии в этой теме: Каков наилучший (идиоматический) способ проверить тип переменной Python?, но мне все еще трудно понять, почему использование abc.Mapping здесь предпочтительнее, чем просто dict.


person Vincent    schedule 29.02.2016    source источник


Ответы (3)


collections.abc предоставляет серию абстрактных базовых классов для контейнера

Этот модуль предоставляет абстрактные базовые классы, которые можно использовать для проверки того, предоставляет ли класс конкретный интерфейс; например, является ли это хешируемым или отображением.

они позволяют вам проверить, имеет ли определенный объект поведение, подобное тому, которое вы проверяете ABC, не обращая внимания на фактическую реализацию.

Например, предположим, что у вас есть функция F, которая что-то делает в соответствии с типом аргумента, вы можете напрямую проверить, является ли он экземпляром списка, кортежа, словаря и т. Д., И выполнять свою работу, но это ограничивает вас только чтобы использовать их, если вы затем создадите свой собственный класс, который имеет аналогичное поведение, чтобы сказать список, в некоторых случаях вы заботитесь и хотите использовать его с F, вы обнаружите, что это не работает, тогда вам нужно изменить F принять ваш класс, но если вместо этого вы проверяете ABC, такая модификация не нужна

Теперь рабочий пример: скажите, что вам нужна функция, которая дает все элементы в четной позиции из списка, тогда вы можете сделать

def even_pos(data):
    if isinstance(data,list):
        return [data[i] for i in range(0,len(data),2)]
    else:
        raise ValueError("only a list")

и использовать как

>>> test = list(range(20))
>>> even_pos(test)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>>

нет проблем, но потом вы понимаете, что кортеж - это то же самое, что и список в том, что касается этой функции, вы также можете добавить эту проверку к функции, и все в порядке, но затем ваш друг сказал вам, что хочет использовать ваш функция, но он использует collections.deque, а затем ваш другой друг сказал ... видишь здесь образец? все упомянутые мной объекты (list, tuple, deque) имеют одно и то же общее, и могут использоваться таким же образом в этой функции примера, и все это поведение сжимается в ABC, поэтому вместо isinstance(data,(list,tuple,collections.deque,...) вам нужно только isinstance(data,abc.Sequence) и функция выглядит как

from collections import abc
def even_pos(data):
    if isinstance(data,abc.Sequence):
        return [data[i] for i in range(0,len(data),2)]
    else:
        raise ValueError("only a Sequence")

>>> even_pos( list(range(10)) )
[0, 2, 4, 6, 8]
>>> even_pos( tuple(range(10)) )
[0, 2, 4, 6, 8]
>>> even_pos( range(10) )  # in python 3 range don't return a list, but a range object
[0, 2, 4, 6, 8]
>>> even_pos( "asdfghjh" )
['a', 'd', 'g', 'j']
>>> 

Теперь вам не нужно знать фактическую реализацию, которая используется, только то, что она имеет желаемое поведение.

person Copperfield    schedule 29.02.2016

Модуль collections.abc предоставляет несколько абстрактных базовых классов, которые можно использовать для общего описания различных типов структур данных в Python. В вашем примере вы проверяете, является ли объект экземпляром абстрактного класса Mapping, что будет верно для многих классов, которые «работают как словарь» (например, у них есть метод __getitem__, который принимает хешируемые ключи и возвращаемые значения, имеют keys, values и items методы и т. Д.). Такие dict-подобные объекты могут наследовать от dict, но в этом нет необходимости.

Абстрактные типы в collections.abc реализуются с использованием модуля abc верхнего уровня. dict register помечен как MutableMapping (который является подклассом Mapping), поэтому проверка isinstance примет словарь как Mapping, даже если Mapping не является фактическим базовым классом для dict.

person Blckknght    schedule 29.02.2016
comment
Это лучший ответ, чем принятый, поскольку в нем упоминается, что collections.abc.Mapping не является фактическим базовым классом dict (вы можете проверить это сами, посмотрев на dict .__ mro__). Было бы даже лучше объяснить, что dict зарегистрировано в качестве значения MutableMapping. - person z33k; 07.08.2020
comment
Метод register задокументирован на странице, на которую я указал. Вот более прямая ссылка конкретно на него. - person Blckknght; 07.08.2020

collections.abc.Mapping является предпочтительным, потому что он определяет абстрактный api для этого типа контейнеров, поскольку dict является лишь реализацией такого контейнера. Немного упрощено, но это ключ - dict не является интерфейсом / abstract / api / ...

Примером объектов, не являющихся экземплярами dict, являются MultiDict, широко используется в веб-фреймворках (например, aiohttp).

person kwarunek    schedule 29.02.2016