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

    −146

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    n = 0
    for i in range(n):
        pass
    print i
    
    NameError: name 'i' is not defined

    Запостил: 3_14dar, 25 Июня 2015

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

    • Всё нормально. Оптимизация. Если цикл выполняется ноль раз, то i не определяется.
      Ответить
      • Ну как, непривычно если думать сишным for циклом. Для foreach-то понятно.

        for (i = 0; i < 0; i++) {
        };

        Тут i будет равным 0
        Ответить
        • И то верно. В сишке начальное присвоение выполнится, даже если тело выкидывается.

          Значит, придётся думать в рамках концепции foreach.
          Ответить
        • А если i прямо в теле фора определить, то вообще не скомпилится.
          Ответить
        • ну ты сравнил! Ты тут явно указываешь изначальное значение (i = 0), а в пистончике не указываешь
          Ответить
          • Оно воспринимается просто как "сишный фор".
            Ответить
            • Ну сам себе злобный буратина. Никто не говорил, что это сишный фор. Более того, использование range в качестве сишного фора считается типичной новичковой ошибкой.
              Ответить
              • Новичковая ошибка это range с индексацией для итерации по списку вместо foreach
                Ответить
      • Я бы сказал по-другому: дефинишен переменной происходит 0 раз. А поскольку декларейшен и дефинишен в пайтоне совпадают, то и декларейшена не происходит. Всё логично.
        Ответить
        • Если мыслить форичем - все нормально, т.к. из пустой коллекции нельзя достать элемент (или он должен был быть none?). Если фором - то возникает разрыв.
          Ответить
          • Ну конечно нельзя. С чего там быть нону? Коллекция с ноном и пустая это две большие разницы
            Ответить
            • На ровном месте возникающая referenceerror ломает мозг. В питоне не хватает явных объявлений переменных.
              Ответить
              • Ну это же его корная фича. Сначала вы хотите явной декларации, потом указания типа, затем профайлера, и наконец компилятора под ллвм
                Ответить
                • Даже в перле до этого дошли
                  Ответить
                  • Я вот не уверен что это хорошо, потому что если ты разрешаешь декларировать переменную не указывая начального значения, то совершенно не понятно что делать в случае обращения к ней:

                    1) падать с треском?
                    2) добавлять undefined?
                    3) автоматически инициализиовать None
                    ?
                    Ответить
                    • к чему столько вариантов? только 1.
                      Ответить
                    • В перле 3. можно так и сделать. Алсо, без обьявления переменных нельзя сделать вменяемые области видимости.
                      Ответить
                      • В питоне области видимости вполне себе вменяемы.
                        Но смех в том, что если ты _читаешь_ переменную, и такая переменная есть в верхнем скопе, то ты обращаешься к ней. А если пишешь -- то создаешь новую переменную, если только не указал global.

                        Так что скопы есть, просто они немного необычны
                        Ответить
                        • Например, нельзя внутри for /if определить переменную
                          Ответить
                          • так и в JS нельзя;)
                            Ответить
                            • В js же есть var
                              Ответить
                              • и что?
                                Ответить
                                • Он что, нерабочий?
                                  Ответить
                                  • Только функции образуют области видимости в JS. Блоки IF не образуют.

                                    var foo = 1;
                                    if (1 == 1){
                                    	var foo;
                                    	foo = 2;
                                    }
                                    alert(foo); /*Будет 2. Хуй а не область видимости*/


                                    Единственный из языков с фигурной скобочкой который так делает. Нежданчик (один из немногих) для сишников, джавистов, пхпистов итд.
                                    Ответить
                                • Сема осваивает js. Добрался до определения переменных.
                                  Ответить
    • Из той же области

      spam = 1
      
      def eggs():
          spam += 1 # UnboundLocalError: local variable 'spam' referenced before assignment
      
      eggs()
      Ответить
      • Но:
        def wtf():
            spam = 1
            def eggs():
                spam += 1
            eggs()
        wtf()

        отработает
        Ответить
        • А это почему?
          Ответить
          • Черт. Странно, я был уверен что замыкания в питоне работают...
            Ответить
            • Замыкания в Питоне работают. Но нужно явно указывать, что захватить из родительского контекста (почти как в PHP со словом use). См. замечание Борманда.
              Ответить
            • Если загуглить ошибку -найдешь почему так сделали. Во всем виновато отсутствие явного объявления.
              Ответить
              • Итого мы имеем две концепции:
                1. В языках со словом var по умолчанию всё наследуется, а var указывает на локальную видимость.
                2. В языках без слова var по умолчанию ничего не видно, поэтому нужно явно указать, что передавать (nonlocal в Питоне, use в PHP).

                Ответ на вопрос, какая из концепций «более правильная», оставим любителям холиваров.

                Говно ваш "ЭКМАскрипт", переходите на "PHP".
                Ответить
                • На чтение в питоне видно все, а вот как знать куда писать? Вот и приходится указывать явно.
                  Ответить
                  • Кстати, эта фраза и для жс справедлива. Тоже надо указывать, куда писать.
                    Ответить
                    • Пользуясь случаем, спрошу, а нужно ли в js объявлять переменные и зачем?
                      Ответить
                      • Да, для того, чтобы они валялись не в верхнем скоупе, а в текущем. Ну и для читающих код.
                        Ответить
                        • А присваивание без объявления вываливает их в корневую область видимости?
                          Ответить
                          • yeap. в браузере `x = 13` равносильно `window.x = 13`
                            Ответить
                          • Но не в строгом режиме, там либо придётся явно писать window.x, либо var x.
                            Ответить
                            • Ясно. А что в жс образует области видимости?
                              Ответить
                              • функции.
                                Ответить
                                • ну и обьекты
                                  Ответить
                                  • Классы?
                                    Ответить
                                    • А они в жс есть?
                                      Ответить
                                      • Я ебу? Но объект это обычно экземпляр класса.
                                        Ответить
                                        • хочешь быть передовым - сей квадратно-гнездовым?
                                          Ответить
                                          • Что, простите?
                                            Ответить
                                            • В жс прототипное программирование
                                              Объекты есть, а классов нет. Не завезли
                                              Ответить
                                            • Если тебе в JS нужен объект, то ты создаёшь пустой объект и присобачиваешь к нему поля и методы. Если же тебе нужны ещё экземпляры того же класса, ты просто клонируешь однажды созданный объект столько раз, сколько экземпляров нужно.

                                              Объявить новый тип данных/класс в JS нельзя.

                                              google: классы в javascript

                                              Найдёшь целое поле, засеянное костылями и велосипедами.

                                              P.S. Домашнее задание: написать, как в JS будет выглядеть аналог наследования.
                                              Ответить
                                              • тоже блядское клонирование с добавлением новых членов
                                                Ответить
                                              • Тупо замена методов? А вот как super() вызывать?
                                                Ответить
                                                • Через цепочку прототипов или напрямую, если известно имя:
                                                  function Base(){}
                                                  Base.prototype.func = function(){ return 'base'; };
                                                  
                                                  function Derived(){}
                                                  Derived.prototype = new Base;
                                                  Derived.prototype.func = function(){
                                                    // Если нельзя напрямую, Base и Derived можно вывести:
                                                    // var Derived = this.constructor;
                                                    // var Base = Derived.prototype.constructor;
                                                    var super_ = Base.prototype.func;
                                                    return super_.apply(this, arguments);
                                                  };
                                                  
                                                  console.log('func: ' + (new Derived).func()); // func: base
                                                  Ответить
                                              • > Если тебе в JS нужен объект, то ты создаёшь пустой объект и присобачиваешь к нему поля и методы.
                                                > Если же тебе нужны ещё экземпляры того же класса, ты просто клонируешь однажды созданный объект столько раз, сколько экземпляров нужно.
                                                Так можно. А если через new? Как минимум, методы будут присобачены к другому объекту (прототипу) и не скопируются. А поля, определённые в конструкторе скопированы не будут, а создадутся заново вместе с новым экземпляром.
                                                Но и с какой точки зрения? В коде объявление будет выглядеть как класс, при использовании new будет выглядеть как единовременное создание, да и оптимизация в v8, кажется, собирала все поля из конструктора в единую структуру.

                                                > Объявить новый тип данных/класс в JS нельзя.
                                                Но почему? Есть же специальные конструкции вроде function...prototype для определения классов, new для создания экземпляров, а также instanceof для проверки принадлежности к классу. Всё сделано для того, чтобы определить класс и создавать его объекты.
                                                Если же в жс нельзя объявить новый тип/класс, то и в жабе нельзя, поскольку любой тип пользователя там будет просто модифицированным Object, а ключевое слово class - просто для отвода глаз.
                                                Ответить
                                                • Кстати, зачем нужны конструкторы? Вернее, зачем нужны конструкторы, которые могут делать любую фигню (например, добавлять действия баги) кроме конструирования? Хотя, зачем нужны конструкторы, которые позволяют только конструировать? Какой подход уместнее?

                                                  Насколько я помню, в хаскеле можно только конструировать, в крестах - преобразовывать и инициализировать в списке инициализации и творить чушь в теле конструктора, в жс - только творить чушь в теле конструктора.
                                                  Но, насколько я помню, Майерс показывал, что в крестах уместнее использовать список инициализации, а не творить чушь.
                                                  А в жс конструктор без побочных эффектов - высшее благо:
                                                  function Downloader(url) {
                                                    this.url = url;
                                                    this.data = null;
                                                    this.callback = null;
                                                  }
                                                  Downloader.prototype.download = function(){
                                                    ... new XmlHttpRequest ...
                                                  };
                                                  
                                                  function PageLoader(id) {
                                                    Downloader.call(this, '//text/' + id + '.txt'); // 2
                                                    this.id = id;
                                                  }
                                                  PageLoader.prototype = new Downloader(''); // 1

                                                  Здесь за счёт того, что конструктор Downloader только инициализирует данные, а не вызывает для удобства метод download, удалось (1) без побочных эффектов создать объект-прототип класса-наследника (2) без побочных эффектов инициализировать поля класса-родителя для каждого объекта класса-наследника, чтобы они не стали статическими для класса-наследника (3) найти простой масштабируемый механизм наследования:
                                                  // Для каждого наследуемого класса в иерархии:
                                                  function Derived(...) {
                                                    Base.call(this, ...);
                                                    инициализация полей наследника;
                                                  }
                                                  Derived.prototype = new Base(...); // впрочем, тут можно Object.create(Base.prototype)
                                                  // Чем-то похоже на C++:
                                                  class Derived: public Base { ... };
                                                  Derived::Derived(...): Base(...), ... {}

                                                  Выходит, надо упрощать конструкторы до инициализации?
                                                  Ответить
                                                  • Ты сейчас про js или вообще?
                                                    Ответить
                                                    • Вообще. Пока писал про жс, задумался о вечном.
                                                      Ответить
                                                      • Вообще - конструкторы в сочетании с именованными параметрами - оччень вкусная вещь. Ими можно описать любой http запрос. В перле одним конструктором можно создать любой сокет - слушающий, соединение, tcp, udp. http://perldoc.perl.org/IO/Socket/INET.html
                                                        Ответить
                                                        • Почему бы так в питоне не сделать - хз, они скопировали сишное апи и так потом ничего и не придумали, видимо, чтобы пришедшим с сишки людям было удобнее писать си на питоне.
                                                          Ответить
                                                        • В перле вообще любую херню можно в одну строку сотворить. Проблема в том, что избыток гибкости приводит именно туда, где сейчас перл.
                                                          Ответить
                                                  • >> Какой подход уместнее?

                                                    Конструктор должен возвращать экземпляр типа. Если левую питушню можно навесить на левую функцию - навесь. Не усложняй конструктор свой.
                                                    Ответить
                                                  • Впрочем, с другой стороны стоят RAII и прочие вкусные вещи, упомянутые п_идаром... В частности, отслеживание границ функции:
                                                    class Label {
                                                    public:
                                                      Label(const std::string&);
                                                      ~Label();
                                                    private:
                                                      static int level;
                                                      const int n;
                                                    };
                                                     
                                                    int Label::level = 0;
                                                     
                                                    Label::Label(const std::string& name): n(++level) {
                                                      for(size_t i=0; i<n; ++i) std::cout << ' ';
                                                      std::cout << name << " {" << std::endl;
                                                    }
                                                     
                                                    Label::~Label() {
                                                      -- level;
                                                      for(size_t i=0; i<n; ++i) std::cout << ' ';
                                                      std::cout << '}' << std::endl;
                                                    }

                                                    http://ideone.com/kFZ1qx
                                                    А ведь, кажется, в споре одновозвратники упоминали простоту логирования. Но, "нагрешив" императивно в конструкторе и деструкторе, получаем милоту.
                                                    Ответить
                                                  • В крестах у конструкторов особый статус, там они реально умеют делать то, что не доступно другим функциям. Например, следить за тем, какие подобъекты уже сконструировались, и выполнять частичную деконструкцию в случае выбрасывания исключения из конструктора одного из подобъектов. Кроме того, в конструкторы и деструкторы вшита логика изменения vtable для полиморфных иерархий и всяческий стейт для инициализации в случае виртуального ромбовидного наследования.

                                                    В хаски для нетривиальной инициализации есть идиома smart constructor. А родные конструкторы нужны в основном для сопоставления с образцом.

                                                    В большинстве остальных языков конструкторы (включая жабу) - не более чем обычная функция, вызываемая в нужный момент. Кмк, там их можно безболезненно выпилить, как сделали в Go и прочих оберонах.
                                                    Ответить
        • Не работает: http://ideone.com/r92sQ5

          В каком Питоне должно пройти?
          Ответить
          • В третьем, если добавить nonlocal spam
            Ответить
            • Точно: http://ideone.com/i9OQgN

              Я забыл, каким словом захватывается родительский контекст...
              Ответить
              • nonlocal включает для записи те же правила поиска переменной, что и для чтения (искать вверх по скопам пока не найдешь)
                Ответить
                • Поправочка: он не ищет в локальной области видимости и глобальной (уровень модуля). Алсо, им нельзя объявить переменную.
                  Ответить

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