Что может пойти не так, если у операторов есть побочные эффекты?

Я наткнулся на библиотеку Python, которая определяет класс, аналогичный следующему, где оператор >> перегружен, чтобы иметь глобальный побочный эффект:

from collections import defaultdict

class V(object):
    """A Vertex in a graph."""

    graph = defaultdict(list)  # accessed globally

    def __init__(self, label):
        self.label = label

    def __rshift__(self, other):
        V.graph[self].append(other)
        return other

    def __repr__(self):
        return 'V(%r)' % (self.label,)

Библиотека делает это, потому что «она дает пользователю хороший синтаксис» для символического построения сетей путем записи простых выражений, таких как:

a = V('a')
b = V('b')
c = V('c')
a >> b >> c >> c

чтобы построить сеть, в которой a соединяется с b, который соединяется с c, который рекуррентно соединяется сам с собой.

Подобный механизм есть и у операторов +, * и @. Все это поведение задокументировано и ожидаемо пользователем.

Однако могут ли произойти какие-либо странные вещи, когда вы не думаете, что выражение должно быть вычислено, но на самом деле это происходит, или наоборот? То есть предполагает ли когда-либо модель выполнения для Python, что определенные выражения свободны от каких-либо побочных эффектов? Я хочу знать, есть ли какие-либо прецеденты, или пользователь должен принять какие-либо особые меры предосторожности.


person Aaron Voelker    schedule 01.06.2018    source источник
comment
Для меня пахнет ужасно.   -  person juanpa.arrivillaga    schedule 02.06.2018
comment
Что может случиться, так это то, что он ведет себя неожиданно. Потому что кто этого ожидал? А неожиданное поведение — это… ну… неправильно. Что касается ожидания побочных эффектов или их отсутствия, я не думаю, что Python чего-то ожидает, но разработчики ожидают.   -  person zvone    schedule 02.06.2018
comment
@zvone: чтобы прояснить, пользователи должны ожидать этого, потому что это способ соединения вещей вместе с этой библиотекой. Этот синтаксис и вариант использования являются центральными для всех их примеров и документации, поэтому все пользователи будут знать об этом поведении. Мой вопрос был больше о том, могут ли это иметь непредвиденные последствия, которые разработчики не учли.   -  person Aaron Voelker    schedule 02.06.2018


Ответы (2)


Вы конкретно спрашивали о неожиданной оценке или отсутствии оценки. Python не предполагает, что какие-либо определяемые пользователем операторы не имеют побочных эффектов, поэтому он не будет пропускать a >> b из-за плохого предположения, что он ничего не делает. У людей иногда возникают проблемы, когда они предполагают, что определенные функции не делают определенных вещей — например, если предположить, что функция, которая принимает итерируемый объект, не будет пытаться len — но большинство таких проблем в равной степени справедливо для >> или метода.

У вас могут возникнуть проблемы с альтернативными интерактивными средами, делающими предположения — например, я могу себе представить проблемы с завершением табуляции, пытающимся оценить то, что не должно, — но использование модуля в реальной программе Python не должно вызывать таких проблем.

Самое близкое, что я ожидаю от таких проблем в реальной программе, это проблемы приоритета. Например, использование C++ << для печати приводит к таким проблемам, как следующий код:

std::cout << true ? "a" : "b";

который печатает 1 вместо a, потому что << связывает сильнее, чем ?:. Я могу себе представить подобные проблемы, возникающие с этой библиотекой.

person user2357112 supports Monica    schedule 04.06.2018

Я не знаю ни одной другой библиотеки Python, которая делает это.

Одним из примеров чего-то странного или неожиданного для меня является следующее:

a = V('a')
b = V('b')

def do_something(c=a>>b):
    return c>>c

do_something(a)

Можно считать, что это дает странный результат (a >> a и a >> b), поскольку аргумент по умолчанию оценивается независимо от того, используется ли он в конечном итоге.


В качестве другого примера, при использовании try/except любая частично завершенная оценка приведет к неполному набору побочных эффектов, которые нельзя будет перемотать.

try:
    a >> b >> c
except NameError:
    # do something else

Здесь при обработке ошибок может потребоваться учесть тот факт, что a >> b все еще оценивается.


Я не рекомендую пользователям делать ни одну из этих вещей. Я только утверждаю, что если пользователь сделает это, то он может получить поведение, которое будет неожиданным, неожиданным или трудным для отладки.

person Aaron Voelker    schedule 04.06.2018
comment
По моему мнению, pyparsing использует << с побочными эффектами для прямых ссылок. - person user2357112 supports Monica; 04.06.2018