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

    +3

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    class Test(contextlib.ExitStack):
        def __init__(self):
            super().__init__()
            with contextlib.ExitStack() as s:
                self.foo = s.enter_context(Foo())
                self.bar = s.enter_context(Bar())
                self.enter_context(s.pop_all())

    Неужели в питоне нет более адекватного способа описать класс, который держит джва ресурса и корректно их освобождает во всех ситуациях? И эти люди ругают кресты за сложность управления памятью...

    Запостил: bormand, 11 Октября 2018

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

    • SEO
      Ответить
    • Именно поэтому я за «линейные типы».
      Ответить
    • Что-то давненько тут петон не обсирали.
      Ответить
      • петон хороший, не надо ево обсырать
        правда он скучный, неконсистентный, вербозный, с занудной строгой тупизацией, но зато без обязательной статической, так что в нем приколько в рантайме ловить ашыбки типа '1' != 1, а тк отличный
        Ответить
        • >вербозный
          По сравнению с перлом или машинными кодами?

          >с занудной строгой тупизацией
          Пиши на жс/"пхп"
          Ответить
    • Если класс хочет что-то сделать перед смертью то он должен быть контекстом чтобы с ним можно было with.
      Остальное это лоу левел кал

      class Petux:
          def __init__(self):
              self.kurochka = None
      
          def __enter__(self):
              self.kurochka = open("/dev/kurochka", MODE)
              return self.kurochka
      
          def __exit__(self, type, value, traceback):
              close(self.kurochka)
      
      with Petux() as koko:
          # любая упавшая тут хуйня закроет курочку
      Ответить
      • Здесь Foo и Bar - уже такие вот питухи. А теперь, о гуру питона, напиши мне объект у которого 2 поля, а не одно как в твоём примере.
        Ответить
        • И в чем проблема?
          Ответить
          • Дык ты напиши код и мы рассмотрим проблемы в нем.
            Ответить
            • Я не вижу какую пользу мне принесёт изучение этой проблемы, а так я бы с радостью.
              Ответить
              • Тебе не придётся разбираться с этой проблемой в будущем.
                Ответить
                • И зачем её тогда изучать?
                  Ответить
                  • Чтобы не разбираться с ней в будущем?
                    Ответить
                    • Зачем с ней разбираться если мне не придется с ней сталкиваться в будущем? :)
                      Ответить
        • def generate_kuorchkas():
              try:
                  kurochka = open("/dev/kuorchka")
                  kurochka2 = open("/dev/kuorchka")
              except KurochkaException as ka:
                  if kurochka:
                      close(kurochka)
                  if kurochka2:
                      close(kurochka2)
              
              return kurochka, kurochka2
          Ответить
          • Хм, т.е. питон от няшной недалеко ушёл? А мне рассказывали, что это высокоуровневый язык...

            З.Ы. А если close исключение кинет? У вас там вроде на это контракта нет?
            Ответить
            • В няшной нет эксепшенов (кроме микрософтовых SEHов).

              Питон -- скучный. Там мало магии, там все нужно делать вручную. За это его и любят.
              Ответить
              • > Питон -- скучный. Там мало магии, там все нужно делать вручную.

                Говорит тот, кто даже contextmanager не осилил. В питоне полно убогой непонятной магии, и становится больше с каждым годом.
                Ответить
                • Скажи кащепу, с чего ты решил что я не осилил contextmanager? C того, что я предпочел обычный try/catch?
                  Ответить
          • А ещё ты исключение съел. Видимо подразумевался перевброс после обработки?
            Ответить
            • да, ты прав
              ну или надо явно сказать что Kurochka=None, иначе будет ошибка
              Ответить
      • З.Ы. Ну и вот эта ручная реализация enter/exit - низкоуровневый кал по сравнению с высокоуровневым калом contextlib.contextmanager и contextlib.ExitStack.
        Ответить
        • Но пока ты ныл, гость уже все сделал.
          Ответить
          • Он сделал какую-то тривиальную хуйню с одним полем, а мне надо два и более. Или питонисты больше одного файла/сокета/девайса никогда не держат открытыми?
            Ответить
            • Ты тупой? Делаешь с двумя объектами. Вбчем проблема-то?
              Ответить
              • Ну сделай, гуру питона.
                Ответить
              • > в чем проблема
                Создание второго объекта может кинуть исключение. При этом первый уже создан и его надо закрыть. Объясни тупому борманду как это сделать без портянок бойлерплейта.
                Ответить
                • З.Ы. With с джвумя объектами не предлагать, это совсем не то.
                  Ответить
                  • Чому это не то?

                    >портянок
                    Ну какие там портянки, try/except
                    Ответить
                    • > какие там портянки, try/except
                      Слова не мальчика, но жавоёба.

                      Тому что мне надо класс, который внутри себя открывает и держит джва файла. А не чтобы каждый клиент за него их открывал двойным with'ом.
                      Ответить
                      • class Petux:
                            def __init__(self):
                                self.kurochka = None
                        
                            def __enter__(self):
                                with open("/dev/kurochka", MODE) as f1, open("/dev/kurochka2", MODE) as f2:
                                    self.kurochka = f1
                                    self.kurochka2 = f2
                        
                            def __exit__(self, type, value, traceback):
                                # как с исключениями тут хз
                                close(self.kurochka)
                                close(self.kurochka2)


                        >Слова не мальчика, но жавоёба.
                        А тебе лишь бы не как в жаве?
                        Ответить
                        • Ты этот код запускал? With же закроет файлы во время выхода из __enter__. Работать не будет, в общем.
                          Ответить
                • Что будет, если открыть файл и не закрыть его?
                  Ответить
                  • Например, другая программа не сможет его открыть пока не закончится эта. Буфер же питон при закрытии сбрасывает?
                    Ответить
                    • Ну это на винде не сможет, в линухе таки откроется.
                      Ответить
                      • На прыщах можно писать и переименовывать открытый другой прогой файл?
                        Ответить
                        • В общем-то даже удалять.

                          В винде, имхо, более корректно сделано.
                          Ответить
                          • Удалять и на винде можно. Просто файл удалится когда будет закрыт.
                            Ответить
                          • на винде можно по всякому открыть CreateFile, можно и так что можн буде удалить
                            Ответить
                          • > В винде, имхо, более корректно сделано.

                            Нет. Нет ничего более тупого, чем "файл занят другим приложением".
                            Ответить
                            • А ты такой берешь procexp и ищешь у кого хендлер твоего файла
                              и грохаешь
                              Ответить
                            • А лучше случайно удалить файл с которым ты работаешь? unlocker, process explorer, process hacker. Да, не для обычного пользователя.
                              Ответить
                            • А в винде просто для чтения один и тот же вайл можно открыть разными программами?
                              Ответить
                              • Удивительный человек-MSDN, прийди!

                                FILE_SHARE_READ:
                                Enables subsequent open operations on a file or device to request read access.
                                Otherwise, other processes cannot open the file or device if they request read access.

                                If this flag is not specified, but the file or device has been opened for read access, the function fails.



                                FILE_SHARE_WRITE
                                Enables subsequent open operations on a file or device to request write access.
                                Otherwise, other processes cannot open the file or device if they request write access.

                                If this flag is not specified, but the file or device has been opened for write access or has a file mapping with write access, the function fails.

                                FILE_SHARE_DELETE
                                Enables subsequent open operations on a file or device to request delete access.
                                Otherwise, other processes cannot open the file or device if they request delete access.

                                If this flag is not specified, but the file or device has been opened for delete access, the function fails.
                                Ответить
                        • Можно, но в файл, открытый функцией "fopen".

                          У функции "open" можно указать флаги блокировки:
                          http://www.opennet.ru/cgi-bin/opennet/man.cgi?topic=open&category=2

                          Ещё есть "flock" и "fcntl":
                          http://www.opennet.ru/man.shtml?category=2&topic=flock
                          Ответить
    • А причем тут память?
      Ответить
      • При том, что утверждается, что язычки с гц облегчают управление памятью (и это правда). Но вот с другими ресурсами в них не всё так безоблачно.

        З.Ы. В "низкоуровневых" крестах это был бы просто объект с двумя полями.
        Ответить
        • О том что тебе надо, да еще и "with с двумя объектами не предлагать" я слышу вьпервый раз. Вопроса зачем нужен гц у меня как-то еще не возникало.
          Ответить
        • Просто GC нужно делать на уровне ОС, а не рантаймов языков
          Ответить
          • Переписать ядро на "PHP"?
            Ответить
            • На Java тогда уж. В PHP нет нормального GC же.
              Ответить
              • Всё норм. Каждое прерывание будет обрабатываться отдельным инстансом "PHP".
                Ответить
                • @Всё норм. Каждое прерывание будет обрабатываться отдельным инстансом "PHP".

                  т.е. роль операционной системы будет выполнять интерпретатор/рунтиме php?! Да Вы, батенька, тролль.
                  Ответить
                  • А что в этом особенного? Некоторые даже "Андроид" называют операционной системой, хотя по сути это интерпретатор/рунтиме "Java" поверх операционной системы.
                    Ответить
                    • >>это интерпретатор/рунтиме "Java"
                      Чушь какая
                      Ответить
                    • Поверх какой? Прыщей? :)
                      А ничего что там совсем другая модель безопасности (для каждого приложения заводится пользователь "прыщей", права приложениям на основе разрешений), нету разделяемых библиотек, нету свопа (шоб пидор который это придумал в бассейне обосрался)?
                      Ответить
                      • "Прыщей" это вообще ядро.
                        ОС это дистр конкретный
                        Ответить
                        • И на основе какого же дистра прыщей сделан "андроид"?
                          Ответить
                          • антроид сам себе дистр: патченное ядро плюс свой юзерленд
                            Ответить
                            • >плюс свой юзерленд
                              >по сути это интерпретатор/рунтиме "Java" поверх операционной системы.
                              Ответить
                              • А Андроиде нет джавы. Ни интерпретатора, ни рантайма. А юзерленд (libc, например) есть
                                Ответить
                                • А что же там? Та жава чтона нем не жава?
                                  Ответить
                                  • Там ART: Android RunTime
                                    Ответить
                                    • Хуярт.

                                      Да, ART и Dalvik несовместимы с Sun/Oracle JVM. Но что основным языком разработки для ART и Dalvik является Java это не отменяет.
                                      Ответить
                                      • >>является Java это не отменяет.
                                        нет. Официальный ЯП для разработки под андроид другой.
                                        Угадаешь какой?
                                        Ответить
                              • Там можно и нативные приложения писать с помощью NDK. Так, например, работает Qt/Android. Но все равно приложение должно иметь jar, то есть, грубо говоря, делается Java-приложение из одной странички, на которой фреймворк уже рисует все что нужно.
                                Ответить
                                • >> иметь jar,
                                  не надо никакой .jar. Надо .apk.
                                  Ответить
                      • Своп включить в "Андроиде" (ага, в том самом "Линуксе", поверх которого это всё говно запускается) можно.
                        Ответить
                        • И как это сделать? А то меня уже заебали постоянные вылеты приложений от нехватки памяти.
                          Ответить
                          • Зависит от ядра. На некоторых ядрах можешь обломаться.

                            На 4PDA обсуждалось:
                            http://4pda.ru/forum/index.php?act=search&source=all&forums[]=317&query=swap&subforums=1&result=topics
                            Ответить
                            • А, так пердолиться надо?
                              Ответить
                              • Производители устройств с Андроидом ставят на свои изделия пропатченную операционку. На некоторых устройствах достаточно вызова команды swapon в автозапуске (программы типа Swapper, SWAPit RAM EXPANDER сделают это за тебя). На некоторых же нужно патчить ядро (потому что производитель железки мог выпилить своп из ядра) или доустанавливать утилиты.
                                Ответить
              • Хотя ОС в стиле PHP — это весьма свежая идея. После каждого системного вызова она будет перезагружаться.
                Ответить
                • Когда я пользовался "DOS", я выходил из программ кнопкой "Reset". Зачастую на перезагрузку уходило меньше времени, чем на поиск кнопки выхода.
                  Ответить
                • Юзать Die () для выхода иссистемы - отличнейшая идейа!
                  Ответить
                • Толи дело lua, где взяли и написали ОС. Только вот в minecraftе...... И я не уверен что это ОС.
                  https://habr.com/post/272391/
                  Ответить
          • Я эту идею озвучил ещё пару лет назад.
            Ответить
          • структуры ядра и так отлично освобождаются когда умирают использующие их процессы

            что-то я не слышал чтобы процесс умер, а открытый им файл всё еще был открыт.

            рефкаунтинг же
            Ответить
    • - в простеньком скрипте классы не нужны
      - если ты пишешь переиспользуемую библиотеку, то делай все аккуратно ручками, цепочка try-catch и все такое. Чай не нуб, должен понимать, что делаешь, и все предусмотреть (в т.ч. и тупого пользователя).
      - если ты пишешь на Питоне большую программу, то ССЗБ.

      Может, как-то так?
      Ответить
      • > ручками
        Ну блин, я и ExitStack'ом могу. Только наследование от него, наверное, лучше убрать (просто хранить его в поле).

        Я просто не ожидал, что такие тривиальные задачи (открыть и держать джва файла) в этом вашем питоне так сложно решаются. А до 3.3 - ещё хуже.
        Ответить
        • Ну да, как всегда. Указатели - это сложно, давайте уберем указатели.
          Ответить
      • >>если ты пишешь на Питоне то ССЗБ.
        ахахахахахах!!!!
        ППКС
        Ответить
        • Хорошо, а на чем, например, написать... ну, скажем, программку, которая читает бинарные файлы и генерирует массивы на си типа:
          // files.c
          
          #include "files.h"
          
          // "file.bin"
          static uchar file_bin[] = { 0x89, 0x50, 0x4e, ... };
          // "file2.bin"
          static uchar file2_bin[] = { 0x4D, 0x5A,  ... };
          Ответить
          • А нафиг она нужна? Через incbin, имхо, удобней подобную херню цеплять. Да и компилится быстрее.
            Ответить
            • Вот так и знал, что мне предложат готовое решение, которое поддерживает двадцать языков и сто опций, настраиваемое простеньким конфигом на 10 килобайт и собираемое любым компилятором C (с CMake и configure.sh) почти без зависимостей от внешних библиотек.

              (а, так это опция Gnu Assembler) Ладно, спасибо, буду знать.

              Я о другом - предположим, что стоит такая задача (с ходу не нашел примера лучше). На каком языке писать?
              Ответить
              • Можешь хоть на си написать, там кода на экран примерно будет. И в сборку легко встроить, и зависимостей лишних не добавишь.
                Ответить
              • на M4 конечно
                Ответить
                • В M4 нет средств для чтения бинарников... Но можно написать генератор программы на Си, которая будет выполнять поставленную задачу.
                  Ответить
                  • А в "cmake" есть. Значит "cmake" лучше "M4".
                    Ответить
                    • силайновцы именно так и решили
                      теперь они генерят довольно много кода чтобы компилять хеллоуворлд
                      Ответить
                  • Няу крайний случай есть xxd, hexdump, od...
                    Ответить
                    • Кстати, о недесятичном

                      я тут давеча видал код

                      foo & 0377

                      долго думал ЗАЧЕМ
                      Ответить
              • Да вообще ты прав: скрипты (несложные программки из одного файла в 10 строк) удобно писать на скриптовых языках типа питона, руби или перла

                Главное не упрываться и не превращаться в
                https://github.com/reddit-archive/reddit/blob/master/r2/r2/controllers/promotecontroller.py
                Ответить
                • А что в этом примере не так?
                  Ответить
                  • да там всё отлично.

                    Функция, например
                    def POST_create_promo(self, form, jquery, username, title, url,
                                              selftext, kind, disable_comments, sendreplies,
                                              media_url, media_autoplay, media_override,
                                              iframe_embed_url, media_url_type, domain_override,
                                              third_party_tracking, third_party_tracking_2,
                                              is_managed):


                    или вот
                    l=VLink('link_id36'),
                    #...
                    path = ("/api/ad_s3_callback?hmac=%s&ts=%s" %
                                    (signature, _format_expires(now)))
                                redirect = add_sr(path, sr_path=False)
                    Ответить
                    • Сигнатура функции отличная. Была бы автором корпорация Microsoft, было бы ещё столько же параметров с именами типа reserved2 и unused2, в которые нужно помещать NULL. А тут вроде бы даже обошлись без зарезервированных и неиспользуемых параметров.
                      Ответить
                      • Ты не вдупляешь разницу между winapi и питоном?
                        Ответить
                      • >> Была бы автором корпорация Microsoft
                        в win32api обычно очень подробно документированы все параметры. А тут?

                        Кроме того наличие параметра jquery -- подозрительно
                        Ответить
    • СЗОТ
      Заметил странное поведение в терминалах на маке. Если вводить строку кириллицей, например, и во время ввода нажимать backspace (и соответственно видеть, как символ удалился), то потом в строковую переменную попадает какая-то белиберда. Пример (тестовая программа на Go):
      ввожу "выход"
      удаляю последнюю "д"
      ввожу буквы так, что получается "выхооод"
      удаляю "оод" и ввожу "д"

      Вывожу строку, полученную с input'а: "выхо?о?д"

      Это я где-то косячу с кодировками?
      Ответить
      • Ну видимо со стороны клиента utf-8 а на серваке что-то однобайтовое. Вот он и удаляет половину символа.
        Ответить
        • Что ты понимаешь под клиентом и серваком?
          Я просто написал примитивное приложение на Go (а до этого такая же ерунда была с Racket), которое читает инпут и выводит то, что прочитало.

          Походу надо курить что-то типа https://golang.org/pkg/unicode/
          Ответить
          • Клиент = эмулятор терминала
            Сервер = твоя прога
            Ответить
            • Так юмор в том, что, если я не удаляю ничего, то всё работает как надо. То есть, что ввёл на кириллице, то и получил на выходе. Такое впечатление, что неправильно обрабатывается именно backspace.
              Ответить
              • А откуда ты знаешь, что правильно? Какие-то байтики вводит, какие-то выводит... Сортировку или upcase погоняй, скорее всего какую-нибудь хуйню покажет.
                Ответить
                • А потому что я в pet project'е, где у меня это всё вылазит, сравниваю ввод с некоторым набором команд (даже, скажем, парсю при помощи GoParsec), и там легко можно проверить корректность ввода.

                  Я не знаю, может, цари просто вообще не доверяют всяким ReadLine'ам и юзают посимвольный ввод с последующей обработкой.
                  Ответить
                  • >>цари просто вообще не доверяют всяким ReadLine'ам
                    зиш так делает, например)
                    Ответить
    • Ок, раз местные питонобоги обосрались, придётся крестолядям помогать.

      Кратко поясню суть проблемы. Допустим, борманд хочет написать код для ковыряния гит-паков (не спрашивайте, зачем). Гит-пак состоит из джвух файлов: файл с блобами и файл с индексом. Борманд хочет работать с двумя файлами одновременно через нормальный API в виде класса GitPack.

      Обычная питономакака начнёт с чего-то вроде (весь код ниже я не проверял, но суть должна быть ясна)
      name = "path/to/pack"
      with open(name + ".pack") as blobs, open(name + ".idx") as idx:
        # пожалуйста, не юзайте pack снаружи
        pack = GitPack(blobs, idx)
        pack.read_objects() # etc.
      Далее, как бы я сделал это в петоне:
      # Пожалуйста, юзайте эту функцию вместо конструктора
      @contextlib.contextmanager
      def open_git_pack(name):
        with open(name + ".pack") as blobs, open(name + ".idx") as idx:
          # принимает уже открытые ресурсы
          yield GitPack._make(blobs, idx)
      
      with open_git_pack("path/to/pack") as pack:
        pack.read_objects()
      Для сравнения, если для ошибок используются исключения, крестобог написал бы это просто как обычный конструктор
      GitPack::GitPack(const string& name)
        : blobs_(name + ".pack")
        , idx_(name + ".idx")
      {}
      готово, корректная обработка ошибок.

      P.S. Если число ресурсов заранее неизвестно, нужно таки юзать убогий ExitStack.
      Ответить
      • Я думал над этим вариантом вчера, но он тоже странный. Конструктор класса получается побитым на 2 части и одна вообще снаружи.
        Ответить
        • > одна вообще снаружи
          Ну положи в статический метод, если тебя это волнует
          class GitPack(object):
              def __init__(self, blobs, idx):
                  pass # ну вы понели
          
              @staticmethod
              @contextlib.contextmanager
              def open(name):
                  with open(name + ".pack") as blobs, open(name + ".idx") as idx:
                      yield GitPack(blobs, idx)
          Ответить
          • class GitPack:
                def __init__(self, name):
                    with ExitStack() as exit_stack:
                        self.blobs = exit_stack.enter_context(open(name + ".pack"))
                        self.idx = exit_stack.enter_context(open(name + ".idx"))
                        self.exit_stack = exit_stack.pop_all()
            
                def close():
                    self.exit_stack.close()
            
                def __enter__(self):
                    return self
            
                def __exit__(self, et, ev, eb):
                    self.close()
            Как-то так, наверное, сделаю. Хоть и бойлерплейт, но вроде более-менее читабельно, масштабируется на большее количество ресурсов и можно юзать без with (из REPL, к примеру).
            Ответить
            • З.Ы. В принципе, всю требуху можно спрятать в отдельный mixin и юзать как-то так:
              class GitPack(SafeContextMixin):
                  def _safe_init(self, name):
                      self.blobs = yield open(name + ".pack")
                      self.idx = yield open(name + ".idx")
              Ответить
          • Во! Няшно и безопасно.
            https://ideone.com/GAQG5i
            class Foo(SafeContextMixin):
                def _safe_init(self, m1, m2):
                    self.f1 = yield m1()
                    self.f2 = yield m2()
            Ответить
      • > убогий ExitStack
        Или функциональненькую рекурсию...
        Ответить
      • Как всё сложно.
        Ответить
    • Питонисты всё таки редкостные говоруны.

      Однажды кто-то спросил питониста "передаются ли аргументы по ссылке или по значению"?

      Правильный ответ был ты таков: "Всё и всегда передается по значению, но надо учитывать что объекты хранятся в куче, и ты работаешь только со ссылками на них, а значит передавая в функцию ссылку на объект ты даешь функции возможность изменить его, если конечно это не ридонли объект типа туплы или строки. Кстати, листы и дикты это тоже объекты. В общем всё как в жабе".

      Вместо этого развели какую-то кашу:

      When asked whether Python function calling model is "call-by-value" or "call-by-reference", the correct answer is: neither

      што???

      In Python, (almost) everything is an object. What we commonly refer to as "variables" in Python are more properly called names. Likewise, "assignment" is really the binding of a name to an object

      Так референс же!

      That's a lot of terminology all at once, but those basic terms form the cornerstone of Python's execution model. Compared to, say, C++, the differences are subtle yet important

      Правда?
      https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/

      Ну ладно, может быть это единственный случай непонимания, да и вроде бы ему там в комментах напихали хуёв

      Но нет!
      https://www.python-course.eu/passing_arguments.php
      Correctly speaking, Python uses a mechanism, which is known as "Call-by-Object",

      Да чтож такое то? Зачем писать километры мутного текста вместо простого ответа?

      Самое смешное что и официальная дока питона называет это call by assignment (аа!!!) хотя и не так многословно
      Ответить

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