Python: могу ли я написать полиморфный своп на изменяемые объекты?
Этот пост в блоге (в настоящее время обсуждается в Hacker News) гласит:
Есть простой "лакмусовый тест" для того, поддерживает ли язык семантику передачи по ссылке:
Можете ли вы написать традиционный метод / функцию swap (a, b) на языке?
Традиционный метод или функция подкачки принимает два аргумента и заменяет их так, что переменные, передаваемые в функцию, изменяются вне функции.
AFAIK, в Python, "традиционная функция подкачки" на неизменяемых объектах - нет-нет. Но как насчет изменяемых объектов? Из любопытства я написал следующие тесты:
# Pythonic way to swap variables
(l1, l2) = ([1], [2])
(l1, l2) = (l2, l1)
assert (l1, l2) == ([2], [1])
# This doesn't work inside a function,
# since new bindings are created and discarded
def failed_swap(a, b):
(a, b) = (b, a)
(l1, l2) = ([1], [2])
failed_swap(l1, l2)
assert (l1, l2) == ([1], [2])
# Working swap function (procedure) on lists
def swap_lists(a, b):
aux = a[:]
a[:] = b[:]
b[:] = aux[:]
(l1, l2) = ([1], [2])
swap_lists(l1, l2)
assert (l1, l2) == ([2], [1])
# The same thing on dicts and sets, thanks to duck typing
def swap_dicts_or_sets(a, b):
aux = a.copy()
a.clear()
a.update(b)
b.clear()
b.update(aux)
(s1, s2) = ({1}, {2})
swap_dicts_or_sets(s1, s2)
assert (s1, s2) == ({2}, {1})
(d1, d2) = ({"foo": 1}, {"bar": 2})
swap_dicts_or_sets(d1, d2)
assert (d1, d2) == ({"bar": 2}, {"foo": 1})
Итак, кажется, что я мог бы написать несколько специализированных "традиционных перестановок", по крайней мере, для некоторых изменяемых типов.
- Терминология: означает ли это, что Python поддерживает семантику передачи по ссылке для любого изменяемого объекта?
- Если да, могу ли я написать общий "традиционный обмен", работающий с любыми изменяемыми объектами?
1 ответ
Ответом на 1 является что-то вроде "Python всегда использует передаваемые аргументы при постоянной ссылке на объекты".
Ответ 2: нет, вы не можете сделать это в общем виде, чтобы найти несколько контрпримеров, предположим, что вы хотите поменять 2 узла в дереве:
r
/ \
a b
/
c
и скажи что c
знает, что его родитель a
; после того, как вы сделали свой "общий обмен", c
все равно будет думать, что объект a
является его родителем, но a
Казалось бы, у него нет детей и b
рассматривать c
как его ребенок.