Змея / Говнокод #26751 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
import traceback

a = (1,2,[3,4])
try:
    a[2] += [4,5]
except:
    traceback.print_exc()
print(a)

Traceback (most recent call last):
  File "prog.py", line 5, in <module>
    a[2] += [4,5]
TypeError: 'tuple' object does not support item assignment

(1, 2, [3, 4, 4, 5])


Какой бароп )))

https://m.habr.com/ru/company/domclick/blog/506138/

Запостил: 3_dar 3_dar, (Updated )

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

  • Статью не читал, но так то понятно почему:
    Сначала происходит +=, а потом пытается записать в tuple, но объект уже изменён.
    Ответить
    • Итак, списки мутабельны, поэтому к [3, 4] можно добавить [4, 5] и получить [3, 4, 4, 5].

      Кортежи же (туплы) немутабельны, поэтому изменять их нельзя. Однако, список нельзя хранить в кортеже по значению, вместо него хранится ссылка. a[2] — это именно ссылка на список, а не сам список.

      При изменении списка ссылка на него не меняется, потому что сам список меняется на месте. Поэтому списки, лежащие в кортеже, можно изменять.

      Всё верно?
      Ответить
      • Я так думал код эквивалентен следующему:
        x = a[2]
        x += [4, 5]
        a[2] = x
        Ответить
        • Скажу больше: третья строка ничего не делает, потому что в x уже лежит ссылка на список. При изменении списка ссылка на него не меняется.

          Точнее, она генерирует исключение, потому что кортежи иммутабельны.

          Вот реальный пример без третьей строки:
          https://ideone.com/4sxUjo
          Ответить
      • Да.
        «+=» в Путухе работает за счёт магического метода __iadd__(self, ...), который для списка делает «self.extend(...)» и возвращает self:
        >>> a = [1, 2, 3]
        >>> id(a)
        2056768382336
        >>> id(a.__iadd__([4, 5, 6]))
        2056768382336

        Далее интерпретатор пытается присвоить «новое» значение элементу кортежа и ожидаемо обламывается. А поскольку транзакций не завезли — исключение не откатывает изменения в списке.
        В общем-то, налицо багор в рахитектуре, но то, как отбитые путухи в статье пытаются агрессивно отстоять эту хуйню («всё логично», «Но не странно ну вот вообще») — выглядит очень смешно. Один особенный ещё и сишку приплёл, лол.
        Ответить
        • А была бы транзитивная иммутабельность как в D, то такой бы херни не возникло
          Ответить
          • А я за крестовый «const». Очень удобная штука, как ни странно.
            Ответить
              • Я именно за крестовый «const».
                class Huj {
                public:
                    Huj() = default;
                    ~Huj() = default;
                
                    const std::vector<Cell> & getCells() const { return cells; }
                private:
                    std::vector<Cell> cells = {};
                };


                getCells() возвращает ссылку на вектор, но при этом попортить внутренности хуя нельзя: ссылка константна, и std::vector::operator[]() тоже возвращает константную ссылку.
                Для такого же уровня надёжности в каком-нибудь путухе, например, придётся возвращать копию массива, как и в ЙАЖЕ (впрочем, в последней, вроде как, можно какими-то ебанутыми кастами провернуть нечто похожее).
                Ответить
              • Кстати, если есть есть некая петушня
                int foo(...) const {}
                Значит ли это, что она потокобезопасная?
                Ответить
                • Ну если никто не пишет в те данные, которые она читает - то да.
                  Ответить
                  • Но immutable более жесткая гарантия. Позволяет лишь одну инитиализацию.
                    Ответить
                • Нет. Помимо замечания Борманда, есть ещё много способов выстрелить себе в ногу (это же кресты, в конце-концов), например (из очевидного и гарантированно работающего):
                  struct X {
                      // ...
                      int foo(...) const {
                          return x++;
                      }
                  
                  private:
                      // ...
                      mutable int x = 0;
                  };


                  Или вообще как-нибудь так:
                  struct X {
                      // ...
                      int foo(...) const {
                          return const_cast<int&>(x)++;
                      }
                  
                  private:
                      // ...
                      int x = 0;
                  };

                  Это будет работать, если сам объект изначально non-const. А если он объявлен как const — UB.

                  UPD: Или ещё из очевидного:
                  int foo(...) const {
                      static int x = 0;
                      return x++;
                  }
                  Ответить
                  • Хорошо. Если без mutable и без UB, может быть небезопасным?
                    Ответить
                    • UPD-пример. Или ещё:
                      int x = 0;
                      struct X {
                          int foo1() const
                          {
                              return x++;
                          }
                      };


                      Как и сказал Борманд, если функция может читать любые данные, в которые кто-то (включая её саму) может что-то записать — она не потокобезопасна (ну, по-умолчанию, без синхронизации).
                      Ответить
                        • Так const — это вообще ни разу не про потокобезопасность.
                          Ответить
                          • Но если бы внутри const не портили внешний контекст - была бы автоматически потокобезопасность.
                            Ответить
                            • Не было бы. Другие то методы могут быть не const. И даже если тебе передали конст ссылку на объект, это ещё не означает что у кого-то другого нет полноценной ссылки.

                              Т.е. это банальная защита от дурака чтобы случайно контракт не нарушить и не писнуть куда не надо, не более того.
                              Ответить
                              • При чем тут const ссылка? Если метод const, то поебать какая ссылка. Про другой метод понял.
                                Ответить
                                • void foo(const string & str) const
                                  {
                                      lock_print();
                                      print_string(str);  // print_string(const string &) const noexcept
                                      unlock_print();
                                  }

                                  Казалось бы, выгледит потокобезопасно, но вот хуй:
                                  // global
                                  string str = "Hello World";
                                  
                                  // thread 1
                                  foo(str);
                                  
                                  // thread 2 
                                  str = "ByeBye Earth";
                                  
                                  // Вывести может, например, "Hello Earth", или вообще свалиться нахуй
                                  // если в строке произойдут какие-нибудь реаллокации


                                  Чтобы тип ссылки/метода обеспечивал потокобезопасность, компилятор должен гарантировать, что в любой момент времени во всех потоках на один и тот же объект существует либо ровно одна мутабельная ссылка, либо неограниченное количество иммутабельных. Казалось бы, при чём тут ржавчина?..
                                  Ответить
                                    • Нет, не похуй.

                                      Другие то методы могут быть не const. И даже если тебе передали конст ссылку на объект, это ещё не означает что у кого-то другого нет полноценной ссылки.

                                      Т.е. это банальная защита от дурака чтобы случайно контракт не нарушить и не писнуть куда не надо, не более того.
                                      Ответить
                  • Да блин. В этих примерах пишут в те данные, которые читает функция. И похуй, что пишет она сама.
                    Ответить
            • а что странного? жабоебы вон тонны интерфейсов плодят, чтобы достичь того эффекта, котоырй дает крестовый const
              Ответить
      • Затем, что нормальное программирование меня не интересует.
        Ответить
      • А теперь серьёзнее. Гусары, не ржать!

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

        Оказалось, что не так просто создать класс с нумерными полями. На $0, ${0}, ${'0'} парсер обламывается, хотя обычные переменные (не поля класса) можно сделать нумерными. При этом через десериализацию нумерные поля легко создаются.

        Есть вариант через каст из массива ((object)[1,2,[3,4]]), но тогда не работает функция array_push, потому что [3,4] в этом случае почему-то не остаётся массивом, а конвертируется в экземпляр класса, у которого какие-то методы массива не определены.

        А с именованными полями всё работает. В «PHP7» даже анонимные классы завезли:
        <?php
        
        $a = new class { 
            public $a = 1;
            public $b = 2;
            public $c = [3,4];
        };
        
        
        array_push($a->c, 4,5);
        
        var_dump($a);
        Ответить
          • Как вариант можно ещё создать класс, который реализует интерфейс ArrayAccess, тогда можно получить туплу фиксированного размера с нужными свойствами. Но тогда придётся писать много бройлерплейта. В описании класса получится типичная «Йажа».
            Ответить
        • > [3,4] в этом случае почему-то не остаётся массивом

          Так создай stdClass через каст, а потом присвой массив 2-му элементу.
          Ответить
          • Сразу видно, что ты не оператор шаблонизатора. Ты умный.
            Ответить

Добавить комментарий для nemyx Отменить ответ

Где здесь C++, guest?!

    А не использовать ли нам bbcode?


    8