Как проверить "неизменяемость на любой глубине" в Python?

Я определяю объект Python как "неизменяемый на любой глубине" iff

  1. Она (номинально) неизменна;и
  2. если это объект "контейнер", то он содержит только объекты, которые"неизменяемы на любой глубине";
((1, 2), (3, 4)) неизменен на любой глубине, тогда как ((1, 2), [3, 4]) не является (хотя последний, в силу того, что он кортеж, "номинально" неизменен).

есть ли разумный способ проверить, является ли объект Python "неизменяемым на любой глубине"?

относительно легко проверить первое условие (например, используя collections.Hashable класс, и пренебрегая возможностью неправильно реализованного __hash__ метод), но второе условие сложнее проверить из-за неоднородности объектов "контейнера" и средств итерации по их "содержимому"...

спасибо!

4 ответов


нет общих тестов на неизменность. Объект неизменен, только если ни один из его методов не может мутировать базовые данные.

скорее всего, вас интересует хэшируемость, которая обычно зависит от неизменности. Контейнеры, которые hashable будет рекурсивно хэш их содержание (т. е. кортежи и frozensets). Итак, ваш тест сводится к запуску hash(obj) и если это удастся, то он был глубоко hashable.

IOW, ваш код уже использовал лучший тест доступно:

>>> a = ((1, 2), (3, 4))
>>> b = ((1, 2), [3, 4])
>>> hash(a)
5879964472677921951
>>> hash(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Я думаю, вы ищете что-то вроде этого:

def deeply_hashable(obj):
    try:
        hash(obj)
    except TypeError:
        return False
    try:
        iter(obj)
    except TypeError:
        return True
    return all(deeply_hashable(o) for o in obj)

одна очевидная проблема здесь заключается в том, что перебираете dict перебирает свои ключи, которые всегда незыблемы, а не его значения, что вас интересует. Нет простого способа обойти это, кроме, конечно, специального корпуса dict -- что не помогает с другими классами, которые могут вести себя аналогично, но не являются производными от dict В конце, я согласен с delnan: нет простого, элегантный, общий способ сделать это.


Я не уверен, что именно вы ищете. Но, используя ваши данные примера:

>>> a = ((1, 2), (3, 4))
>>> b = ((1, 2), [3, 4])
>>> isinstance(a, collections.Hashable)
True
>>> isinstance(b, collections.Hashable)
True

таким образом, фактически используя collections.Hashable - это не путь. Однако,

>>> hash(a)
5879964472677921951
>>> hash(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Итак, по крайней мере, для примера данных, используя hash достаточно, чтобы проверить, если объект является hashable. Конечно, как вы уже указали в своем вопросе, если __hash__ неправильно реализован для подкласса, скажем, list, то эта проверка не будет работать.


это абсолютно имеет смысл, чтобы иметь такой тест!

Рассмотрим время "deepcopy () - ing" (или вручную клонировать () - ing) объект против простого задания ссылки!

представьте, что два объекта должны владеть одним и тем же объектом, но полагайтесь на то, что он не изменен (хорошим примером являются dict-ключи).

тогда безопасно использовать только ссылочное назначение, если и только если неизменность можно проверить.

Я бы рассмотрел рекурсивно проверить, что-то вроде

def check(Candidate):
    if isinstance(Candidate, (str, int, long)):
        return True
    elif isinstance(Candidate, tuple):
        return not any(not check(x) for x in Candidate)
    else:
        return False