1. Python / Говнокод #12715

    −105

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    def f(x):
        return x.strip()
    
    lines = map(f, open("1.txt", "r"))
    open("1.txt", "w").write(" ".join(lines))

    ХАСКЕЛЕПРОБЛЕМЫ™. Теперь и в питоне.

    Исходный файл содержит 3 строчки:

    just
    as
    planned
    Питон 2.7:
    $ python2.7 1.py
    $ cat 1.txt
    just as planned
    Питон 3.0:
    $ python3.2 1.py
    $ cat 1.txt

    Запостил: bormand, 09 Марта 2013

    Комментарии (42) RSS

    • О боже... почему [code][/code] в описании так страшно выглядит...
      Ответить
    • lines = map("".__class__.strip, open("1.txt", "r"))

      Можно и без дополнительных функций.
      Ответить
      • > "".__class__.strip
        Большое спасибо, не знал такой фишки.

        Но хаскелепроблема то не в этом ;)
        Ответить
        • (нуб в хаскелях/путхонах) дай угадаю: где-то там возвращается итерируемый объект, вместо непосредственно самого результата?
          Ответить
          • Да, все верно, в питоне 3 мап (да и не только мап) стал ленивым и возвращает хрень с итератором. В принципе, задумка хорошая, экономит память и проц, особенно если не читать результат до конца...

            Но вот на случаях, когда первый генератор имеет побочный эффект теперь получается вот такой вот фейл. В данном случае питон успел потереть файл вторым open'ом задолго до того, как прочитал из него первую строку.
            Ответить
            • Это к тому же шеллопроблемы, так что все ОК.
              myaut@panther:/tmp> echo test > file.txt
              myaut@panther:/tmp> cat file.txt | grep test 
              test
              myaut@panther:/tmp> cat file.txt | grep test > file.txt
              myaut@panther:/tmp> cat file.txt
              myaut@panther:/tmp>
              Ответить
              • > так что все ОК
                Да не совсем как бы ОК... скрипт работал-работал, попробовал портануть под python3 - начал херить файлы. Все-таки в питоне3 полно несовместимостей с двойкой.

                > Это к тому же шеллопроблемы
                Да я в курсе, еще когда с линуксом только-только начинал знакомиться нарвался на эту траблу, только вместо grep'а был sed... Кстати если обобщить - это проблема любых ленивых конвейеров.
                Ответить
                • > Все-таки в питоне3 полно несовместимостей с двойкой.
                  А мне кажется, что это очень и очень годно. Даже несмотря на кучу похеренного софта. Даже несмотря на необходимость выучить принципиально новый язык.
                  Это того стоит.
                  Ответить
                • > Да не совсем как бы ОК... скрипт работал-работал, попробовал портануть под python3 - начал херить файлы. Все-таки в питоне3 полно несовместимостей с двойкой.

                  А в вашем мире тестов не придумали?
                  Ответить
                  • > А в вашем мире тестов не придумали?
                    Признайтесь, вы всегда пишете тесты для не mission-critical утилит в 20 строчек?
                    Ответить
              • "Это к тому же шеллопроблемы"

                не правда. к шелу данная проблема относится очень слабо.

                $ cat file.txt | grep test > file.txt


                шел интерпретирует сроку комманды в один проход, в следующий проход открывает файлы, в следущий проход запускает комманды. поэтому cat всегда будет видеть пустой файл. ничего не lazy, все очень детерминистично.

                да и каждый админ знает что "> filename" в шелле есть самый быстрый способ обнулить файл.
                Ответить
            • "в питоне 3 мап (да и не только мап) стал ленивым и возвращает хрень с итератором."
              "когда первый генератор имеет побочный эффект теперь получается вот такой вот фейл."

              ацтой.....

              теперь я понимаю почему после путхона 3го, народ начал еще более активно форкать 2ую версию.
              Ответить
              • > ацтой
                Всем давно известно, что ленивая обработка данных весьма хреново сочетается с побочными эффектами. Я за то, чтобы map был ленивым. Хочется неленивой обработки - есть for и list comprehensions.
                Третий Python устранил много фейлов второго, и когда-нибудь его вытеснит. Бубунта последняя вроде на тройку окончательно перешла.
                Ответить
                • > Хочется неленивой обработки - есть for и list comprehensions.

                  Но нет неленивого map'а.

                  > Третий Python устранил много фейлов второго [...]

                  Но как я вижу - так же добавил новых.

                  Как я на эту тему не думаю, все равно не могу себе представить жизнь на "there is only one true way to do this" языках.
                  Ответить
                  • > Но нет неленивого map'а.
                    Ну можно вот так делать, если ленивость мешает:
                    lines = list(map(f, open("1.txt", "r")))
                    Ленивость в мапе то фишка то хорошая, годная. Просто за что не люблю питон - 3.0 и 2.0 не совместимы чуть более чем полностью.

                    Отношение Гвидо к разрабам: "Мы пилим новый убер-язык, идите нахуй со своими наработками".
                    Ответить
                  • > не могу себе представить жизнь на "there is only one true way to do this" языках
                    Прекрасно. К тому же, на деле всегда there is more than one way (особенно, когда дело уходит дальше hello world), но есть среди них есть самый краткий, читабельный, понятный и идиоматичный.
                    Ответить
        • ХЗ. Я результат вижу, но понять почему так происходит не могу. Больше похоже на баг, чем на какую-то странную задумку.

          Или join теперь использует yield?
          Ответить
        • А... все, понял, map() теперь что-то другое возвращает... мда :|
          Ответить
        • > "".__class__.strip
          Мне кажется, str.strip выглядит понятнее
          Ответить
          • __class__ более универсально. Не обязательно знать какой класс.

            Но на самом деле добиться полного соответствия изначальному коду не удасться, т.е. если бы у нас был еще какой-нибудь Foo.strip, то ничего бы не получилось.
            Ответить
        • ну тогда уж str.strip
          Чорд, не дочитал.
          Ответить
      • Кстати, сейчас подумал внимательно. Коды не эквивалентны.

        В моем говнокоде с отдельной функцией работает duck-typing, и мапать можно как обычные строки, так и юникодные, так и вообще все что угодно, лишь бы у него был strip().

        В вашем же случае всегда вызывается метод класса str, с вот такими последствиями: TypeError: descriptor 'strip' requires a 'str' object but received a 'unicode'. Хотя в данном конкретном случае все будет работать.
        Ответить
        • тогда лучше лямбду туда запихнуть
          map(lambda x: x.strip(), open("1.txt", "r"))
          Ответить
          • Ну в настоящем коде функция была не такой тривиальной, и в одну строчку ее загонять не стоит ;)
            Ответить
            • Ещё вот так можно ради забавы
              def trigger(func):
                  def _trigger(x): return getattr(x, func)()
                  return _trigger
              
              map(trigger('strip'), open('1.txt', 'r'))
              К вопросу о "there is only one way to do it"
              Ответить
    • Писать в тот же файл, из которого читаем - в любом случае ССЗБ. По науке нужно записать в другой файл, а затем переименовать его в исходный. Надеюсь, не нужно объяснять, почему?
      Ответить
      • Конечно, но файл был не настолько ценный, чтобы над ним подобную транзакцию гонять ;) Это было всего лишь небольшое допиливание мейкфайла.

        А насчет в любом случае не согласен. С большими двоичными файлами часто такое не прокатит по соображениям производительности и места на диске.
        Ответить
        • > С большими двоичными файлами часто такое не
          прокатит по соображениям производительности
          и места на диске

          а вы представьте, что надо зашифровать/дешифровать/упаковать/распаковать файл, или любую другую трансформацию. и в процессе ее оказывается, что входящий блок меньше исходящего... а файл, по определению, не резиновый, и исходящий блок просто потрет следующий входящий - эпикфейл.
          так что местом на диске придется запастись... что касается производительности, то операция переименования/перемещения файла весьма дешева (в сравнении, скажем, с самой трансформацией, или тупо копирования).
          поэтому архиватором @bormandа пользоваться не буду o_O
          Ответить
          • Здесь имелось в виду частичное обновление большого двоичного файлика (кусками, страничками и т.п.). Есть задачи, в которых нужно поправить пару страничек в двоичном файле. Не копировать же его ради этого его целиком...

            > а вы представьте, что надо зашифровать/дешифровать/упаковать/распаковать файл, или любую другую трансформацию
            > поэтому архиватором @bormandа пользоваться не буду o_O
            А я и не предлагаю такую методику для подобных трансформаций ;)

            А сам юзал запись в тот же файл без транзакции через переименование т.к. файл все равно временный, даже если он похерится или запишется не до конца - хрен бы с ним, в следующий раз заново сгенерится. А лишнюю строчку с переименованием писать было влом.
            Ответить
            • наверное, я параноик, но мне не все равно, похерила ли прога файл, или нет. даже если файл временный, он же для чего-то нужен, и нужен правильный.
              я не буду рад проге, которая ни результат не сделает, да еще и мои файлы похерит. а все потому, что автор поленился написать одну строчку.
              Ответить
              • > наверное, я параноик
                Да я на самом деле не меньший параноик, но в данном случае (костыль, допиливающий пару строк в мейкфайле сразу после его генерации) проблем не вижу. Да и в старом питоне сфейлиться он мог только если место на диске кончилось, или при краше во время записи, что довольно маловероятно, и исправляется перегенерацией.
                Ответить
                • > только если место на диске
                  кончилось, или при краше во время записи

                  или так сложились звезды. иногда вылетит Error - не поймаешь фейлится там, и по такой причине, откуда совсем не ждешь
                  Ответить
            • P.S. Ну и да. В питоне 2.7 файл полностью загружался в память, обрабатывался и сохранялся. Проблемы могли бы быть разве что при краше или окончании места на диске во время записи. В питоне 3.0 map стал ленивым, поэтому данный алгоритм будет фейлиться всегда (в лучшем случае не сможет открыть файл во второй раз, в худшем - потрет его перед чтением).
              Ответить
              • считаю поведение 3его питона логичным, обоснованным, и правильным.
                к тому же, если это небольшой мейк, то можно его сначала полностью считать в память.
                Ответить
                • Ну если делать через 2 файла с переименованием - можно и не читать полностью, от ленивости третьепитона будет только профит.
                  Ответить
    • Как же питон опустился(или поднялся?) до уровня хаскеля.
      Ответить
    • Хуле тут непонятного? Объясняю: незачем иметь по 2 функции вроде range и xrange, одна из которых возвращает список, а вторая - итератор. Оставляем только вторую, кому нужен список - вызовут list.

      Теперь по поводу перехода на третий питон. Радуйтесь своему спиду. Хотели суперпупер динамический язык, где в принципе нельзя даже переменную обьявить (аналог use strict)? Где к переменной можно обратиться как к var или как к "var"? Получите, а теперь резво радуйтесь, что статический анализ и рефакторинг не работают вообще никак и писать на этом уебище без тестов вообще нельзя, т.к. отвалиться может вообще все, что угодно и выяснится это только в момент выполнения того участка кода.
      Ответить
      • > писать на этом уебище без тестов вообще нельзя
        Я всё больше убеждаюсь, что без тестов нельзя писать вообще ни на чём.
        Ответить
        • У меня был случай, когда я отправил клиенту прогу, а она у него свалилась, потому что я сложил число и строку (в яве так можно), у меня все работало, т.к. было другое окружение (код был спрятан за if). Языки со статической типизацией такую хрень хоть в статике отлавливают.

          Ну а кто пишет бинарную (де)сериализацию без тестов, тот ССЗБ.
          Ответить

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