Пайтон: замкнувшее замыкание

Есть немало вещей, которые мне не нравятся в Пайтоне. Не так давно мы на работе наткнулись ещё на одну.

У нас есть код, который выглядит примерно так (можно написать и одну функцию, но так нагляднее):

def outer(useless = None):
    def inner():
        if useless is None:
            print "Pichal'ka"

    return inner

outer()()

Ничего необычного, одна функция определяется внутри другой, использует переменную используя переменную из внешней функции. Всё логично, всё работает.

Теперь меняем код:

def outer(useless = None):
    def inner():
        if useless is None:
            print "Pichal'ka"

        # тут много кода, начало мы уже потеряли
        if False: useless = None

    return inner

outer()()

Запускаем код и видим (Пайтон 2.6.5):

Traceback (most recent call last):
  File "sample.py", line 10, in <module>
    outer()()
  File "sample.py", line 3, in inner
    if useless is None:
UnboundLocalError: local variable 'useless' referenced before assignment

Какими бы благородными мотивами не руководствовались создатели Пайтона, мне такое поведение не нравится. Вдруг я забыл как переменная попадает в функцию, присвоил ей что-нибудь и получай ошибку в каком-нибудь редком случае.

Избавиться от эффекта можно, изменив прототип внутренней функции на «inner(useless=useless)».

То же, кстати, происходит при импорте модуля:

import random

def outer():
    print random.choice
    #import random

outer()

Раскомментируйте строчку со вторым импортом и получите ошибку.

Все эти ошибки возникают из-за того, что интерпретатор специальным образом анализирует тело функции перед её запуском и только те переменные которые не задаются внутри функции связываются с лексическим окружением функции.

Я не придумал хороших причин так делать с точки зрения языка (может их кто-то из читателей тут меня поправит?), поэтому считаю, что так сделали для упрощения интерпретатора.

42 комментария

Сколько есть способов восстановить chmod?

Иногда на собеседованиях спрашивают такую задачу, на соображалку: предположим вы случайно сняли права исполнения с команды chmod. Как их восстановить?

Я решил подумать сколько способов исправить эту ситуацию я знаю.

Первый способ — скопировать chmod с соседней машины при помощи scp, sshfs и т. п. Если такая машина имеется, конечно.

Способ второй:

cp /bin/sh ~/chmod2
cat /bin/chmod > ~/chmod2
sudo ~/chmod2 +x /bin/chmod
rm ~/chmod2

Третий способ (при условии, что у вас какой-то дистрибутив, использующий пакеты, у меня способ для Debian):

sudo apt-get --reinstall install `dpkg -S /bin/chmod | cut -f1 -d:`

Способ четвёртый:

gcc -x c - <<CHMOD
#include <sys/stat.h>
main(){chmod("/bin/chmod", S_IXUSR | S_IXGRP | S_IXOTH);}
CHMOD

sudo ./a.out
rm a.out

Кто ещё знает способы?

28 комментариев

Группа → служба

Стал руководителем службы разработчиков внутренних сервисов «Яндекса».

10 комментариев