Забавно иногда открывать для себя лаконичные способы делать что-то в синтаксисе нового языка. Сегодня возникла нужда поменять значения двух переменных, и я подумал, что традиционный способ:

temp = x
x = y
y = temp

... слишком длинен. Не может быть, чтобы в Питоне не было чего-нибудь покороче.

В Питоне есть тип данных, который называется "tuple" (и который в руском языке должен называться "кортеж" — слово, возможно знакомое тем, кто изучал в институте высшую математику и что-то помнит из тех времен). По сути он представляет собой пачку любых значений в определенном порядке, с которой можно работать, как с единым целым, например в функцию передать:

x = (1, 'abc', 3)    # tuple
some_func(x)

С этими кортежами связана интересная особенность оператора присваивания. Он может автоматически распаковывать и запаковывать несколько значений в кортежи и обратно:

x, y, z = (1, 2, 3)   # x = 1, y = 2, z = 3
t = 1, 2, 3           # t = (1, 2, 3)

Вот это мне и приглянулось для обмена значениями. Выглядит, по-моему, просто чудесно:

x, y = y, x

Комментарии: 20

  1. Homo-Adminus

    Насколько я помню, в перле всегда была такая возможность :-)
    ($x, $y) = ($y, $x);

    Очень часто еще используют аналогичные схемы для заполнения набора переменных значениями из кусочков строки:
    ($x, $y, $x) = split /какой-нить разделитель/;

  2. Лёхха

    а как насчет массивов? :)
    в перле очень похож обме на листы:
    ($x, $y) = ($y, $x);

  3. Homo-Adminus

    Осталось еще, чтобы ПХПшники прибежали и начали свои варианты предлагать :-)
    А потом можно будет организовать проект по поиску самых красивых/лаконичных/етс методов обмена значениями переменных.

  4. kpumuk

    Я буду за ПХПшника :-)

    list($a, $b) = array($b, $a);

  5. MkDir

    Кстати, насчёт ПХПшников. Я показал своим коллегам эту возможность и они тут же ответили мне:
    list($b, $a) = array($a, $b);

    Это просто ужасно! =)

    Кстати, вот приём на Python'e, до которого я сам дошёл. cursor.fetchall() возвращает словарь. В моём случае он выглядел так:

    {"ref_link": "www.somesite.com", "ref_link": "www.ya.ru", "ref_link": "www.rambler.ru"}

    нужно было получить список ["www.somesite.com", "www.ya.ru", "www.rambler.ru"]
    вот строчка из программы:

    return [link["ref_link"] for link in cur.fetchall()]

  6. elide

    2MkDir
    я вот как-то очень сильно не понимаю, как в питоне можно создать словарь c несколькими значениями под одним ключом.
    >>> a = {"ref_link": "www.somesite.com", "ref_link": "www.ya.ru", "ref_link": "www.rambler.ru"}; >>> a {'ref_link': 'www.rambler.ru'} >>>
    можно это как-то разъяснить?
    кроме того for link in {.....} будет перебирать ключи словаря. ключ словаря в данном случае - строка. потом с этой строкой сделать сделать link["ref_link"] - никак нельзя, потому как "string indices must be integers".....

  7. Merle

    Есть универсальный для всех языков метод через xor :)

    a ^= b
    b ^= a
    a ^= b

    Описание:
    http://wikisource.org/wiki/XOR_swap_algorithm

  8. j2a

    fetchall() всегда возвращает список (точнее кортеж). В зависимости от настройки это либо список словарей, либо список списков.
    Т.е. не
    {”ref_link”: “www.somesite.com”, “ref_link”: “www.ya.ru”, “ref_link”: “www.rambler.ru”}
    а
    ({”ref_link”: “www.somesite.com”}, { “ref_link”: “www.ya.ru”}, { “ref_link”: “www.rambler.ru”})
    Причем это поведение не по умолчанию, а по умолчанию fetchall() возвращает список списка:
    (( “www.somesite.com”, ), ( “ref_link”: “www.ya.ru”, ), ( “ref_link”: “www.rambler.ru”, ))

  9. наблюдатель

    2Merle
    А для строк? или вообще для нецелочисленных ;)

  10. Merle

    2наблюдатель
    Не вижу проблемы. Строка - такая же последовательность 0 и 1, которую также можно несколько раз xor.

  11. TeXHaPb

    Осталось еще, чтобы ПХПшники прибежали и начали свои варианты предлагать :-)

    А можем и предложить. И покрасивее!

    $a = $b + ($b=$a)*0;
    

    Прошу заметить, что ничего не надо "автоматически распаковывать и запаковывать".

  12. наблюдатель

    2Merle
    Это не меняет сути - метод универсален только для целочисленных типов. Возьмите float'ы, object'ы или язык/тип, где строка является последовательностью символов, а не байтов. Метод работать не будет (а ведь именно универсальность Вы подчеркивали).

  13. Merle

    2наблюдатель
    Вы очень серьезно восприняли мою шутку об универсальности.
    :) данный метод я привел скорее в качестве забавного и универсального на низком уровне примера.
    понятно, что с его помощью можно реализовать и посимвольный обмен строк. Криво, но можно.
    Но в общем-то использование этого метода в 99% задач неоправдано. В той же статье на Википедии на которую я давал ссылку написано что "However on modern (desktop) CPUs, the XOR technique is considerably slower than using a temporary variable to do swapping."

  14. kpumuk

    А можем и предложить. И покрасивее!

    $a = $b + ($b=$a)*0;
    Прошу заметить, что ничего не надо “автоматически распаковывать и запаковывать”.

    Красиво, но не умеет строки :-)
    Предлагаю альтернативу:

    $a = @$b . (($b = a) && 0);
    $a = @$b . (($b = a) ? '' : '');

  15. mike

    простите, я, наверное, чего-то не догоняю, но почему бы просто не написать вот так:

    b,a = a,b

  16. kpumuk

    Так можно написать для питона :-) А мы сейчас ПХП обсасываем....

  17. Helios

    если x и y - скалярные, то зачем третья переменная? :)

    x=x+y;
    y=x-y;
    x=x-y;

  18. oxHH

    Скаляр не unlimited. Можно упереться в перебор при сложении.

  19. qulinxao

    тоже симетрично:

    a=-(a+b)

    b=-(a+b)

    a=-(a+b)

  20. qulinxao

    gcd на питоне кортежного присвоения из http://stackoverflow.com/questions/11175131/code-for-greatest-common-divisor-in-python

    python -c"import fractions,inspect;print inspect.getsource(fractions.gcd);"

Добавить комментарий