![]() | Пишет Honeyman ( @ 2005-09-24 19:52:00 |
| Метки данной записи: | python, программирование |
Про Питон
С изучением Python-а дела складываются не слишком радостно. В ходе изучения нашёл там такую фишку, что сразу задумался, стоит ли мне дальше тратить время на Python, если там такое. Почесав голову, обнаглел и решил спросить прямо в рассылке у девелоперов. После не слишком долгой и слегка нервной переписки выяснил, что они это действительно считают нормальным и исправлять никак не собираются. Более того, со слов Самого Гвидо Ван КенобиРоссума выяснилось, что это появилось в Python-е потому, что так было проще всего реализовать
Сижу сейчас, размышляю, что учить вместо Python-а. Ruby (upd: говорят, там так же)? Erlang? А то и Lisp, на который руки уже давно чешутся?
А, да. Про найденную фишку…
Представьте себе, что в Python-е циклы (for, while) и условия (if), а также их представления в виде list comprehensions (это такие чрезвычайно удобные формы создания списков в Python-е, при которых создаётся реальный (а ещё можно «ленивый» с помощью generator expressions, отличающихся по синтаксису от list comprehensions исключительно видом скобок) список по заданному выражению, определяющему его элементы; например, список из квадратов первых десяти натуральных чисел можно задать как [a**2 for a in range (1, 11)]) не создают нового подпространства имён!
Что, непонятно? Это значит всего лишь то, что переменная цикла после завершения цикла не умирает и продолжает существовать дальше, со значением, которое она приняла в последней итерации цикла. Написали два цикла по разным переменным — дальше в функции или коде живут (и путаются под руками) две переменных, n+1 цикл — n+1 переменная…
Лично я столкнулся с этой проблемой, когда примерно полчаса пытался понять, почему в цикле для разных значений переменной цикла печатаются одинаковые значения одной функции, которая принципиальноодинаковой быть не может. И только через полчаса обратил внимание, что я переменную цикла уже давно переменовал с i на imsi, но внутри цикла, в месте вызова злополучной функции, переименовать забыл, а транслятор мне об этом не подсказал, потому что незадолго до этого цикла я делал другой цикл по переменной i, после чего транслятор посчитал, что переменная i уже определена…
Поняв это, я схватился за голову и стал пробовать все возможные глупости, которые можно сделать при наличии этой «фичи». И все глупости сработали!
Вот несколько примеров, оформленных в виде готового и пригодного к использованию исходника на Питоне:
#!/usr/bin/env python -t
# -*- coding: CP1251 -*-
# Создадим подопытный цикл
a1 = ['a', 'b', 'c', 'd', 'e']
# То, с чего я начал: видимость переменной цикла за пределами цикла
for k in a1:
pass
k = None
# никакой ошибки по причине неопределённой переменной в следующей строке
# не будет ибо переменная k считается определённой и равной последнему
# значению цикла
print "k: %s" % k
# Ещё более страшное: видимость переменной list comprehension-а за его
# пределами, делающая чрезвычайно опасным банальный копипаст его
b1 = [l for l in a1]
print "l: %s" % l # переменная l тоже определена
# Если в list comprehension-е несколько вложенных циклов,..
# это не имеет значения, все их переменные будут видны снаружи
c1 = [(m, n) for m in a1 for n in b1]
print "m: %s, n: %s" % (m, n) # m и n вполне себе определены
# Даже переменные из comprehension-а внутри comprehension-а
# "протекают" в область видимости!
d1 = [o for o in [p for p in a1]]
print "o: %s, p: %s" % (o, p) # даже p определён!
# Что там: если мы просто определим переменную внутри цикла,
# она тоже утечёт наружу
for e1 in a1:
q = 'Test'
print "q: %s" % q # q определён - страшно?
# А теперь барабанная дробь...
for s in a1:
print "s0: %s, " % s,
f1 = [s for s in b1]
# А вот достучитесь теперь до переменной s из цикла по списку a1!
print "s1: %s" % s
Я думаю, не стоит объяснять и демонстрировать, что изнутри for-ов и if-ов переменные точно также «вытекают» наружу. Вытекают, уж поверьте. Нереально засоряя пространство имён основного кода и делающего чрезвычайно вероятными (а они ещё будут и чрезвычайно сложно уловимыми) ошибки, подобные моей — когда используется переменная, которая по привычке после других, более приличных языков, кажется уже деинициализированной.
Но и это ещё не самое забавное. Есть такой популярный класс языков — функциональные языки программирования. И апологеты таких ругаются на наличие в обычных языках… переменных. Мол, переменные плохи тем, что они меняют своё значение в течение работы программы, что может вызвать неприятные сайд-эффекты, когда ты пользуешь не то значение, которое хотел. Мол, в одном и том же месте программы при типичном использовании языка (т.е., безо всяких хакерских триков) одна и та же переменная может принимать различные значения. Программистам на других языках это уже должно быть привычно,… но если вдуматься, это на самом деле нелогично.
Я ещё не настолько матёр, чтобы обходиться без переменных вообще, поэтому бесстыдно пользуюсь ими. Но в случае с Python-ом всё ещё страшнее.
Взгляните на следующий код:
if a == b:
j = f(7)
print jj в момент попытки печати может быть не только неясное/плавающее значение, задаваемое функцией f() — у неё может быть плавающий статус определённости!