[python] Давайте писать всякие не очень очевидные для новичков примеры

SCIF32


def f(a, L=[]):
L.append(a)
return L

print f(1)
print f(2)
print f(3)

выводит:
[1]
[1, 2]
[1, 2, 3]

tokuchu

def f(a, L=[]):
Это так "интуитивно понятно" замыкание выглядит?

conv3rsje

Это известная засада с параметрами по умолчанию :(
Надо твёрдо понимать, что они инициализируются один раз, поэтому, к сожалению,
приходится либо делать их None, а потом ставить нужное значение, либо явным образом копировать.
Последнее тоже не всегда прокатывает.

tipnote

Ну, это засада только с мьютбл типами, есс-но, а потому бывает относительно редко. И связано с тем, что копируются ссылки, а не объекты. Ну и это касается не только параметров по умолчанию, а всего мьютбл, что инициализируется на этапе объявления, а не в "рантайме". Например, статические переменные, которые после наследования останутся прежними во всех классах и во всех представителях этих классов
А по теме - гугл "Python Warts".

conv3rsje

Ну, это засада только с мьютбл типами, есс-но, а потому бывает относительно редко
У кого как, я на arg=[] напарывался несколько раз, теперь None ставлю и присваивание через arg or [] делаю в таких случаях.
Еще один раз было невозможно перехватить вывод у функции, подменяя sys.stdout, ибо она его использовала как значение по умолчанию.

yroslavasako

тут история моих мытарств.
Выводов два.
1. В питоне 2.6 переменные list comprehension находятся в родительской области видимости и переписывают значения

z = 4
s = [z*z for z in range(256)]
print z

вернёт не 4 как ожидалось, а 255.
В третьем питоне пофиксили.
2. В питоне 3, как и во 2, применяется динамическое связывание замыканий, что приводит к странным и неожиданным результатам (смотри тред в начале топика). Лучше вообще замыкания не юзать, наверное. А если хочется замыканий - stackless python или PyPy тебя ждут.
Ну и вообще, культуры программирования в питон среде не предусмотрено. При минимальных отличиях синтаксиса второго и третьего питона, на последнее большинство лаб перелезть без затрат так и не смогло, потому как поставило себя в зависимость ото всяких мелких багов (ну или фич - как смотреть вроде того, что я демонстрировал с list comprehension

tipnote

2. В питоне 3, как и во 2, применяется динамическое связывание замыканий, что приводит к странным и неожиданным результатам (смотри тред в начале топика). Лучше вообще замыкания не юзать, наверное. А если хочется замыканий - stackless python или PyPy тебя ждут.
Ну и вообще, культуры программирования в питон среде не предусмотрено. При минимальных отличиях синтаксиса второго и третьего питона, на последнее большинство лаб перелезть без затрат так и не смогло, потому как поставило себя в зависимость ото всяких мелких багов (ну или фич - как смотреть вроде того, что я демонстрировал с list comprehension
2. Прежде чем использовать что-то, стоит почитать, как это работает. Проблема новичка в питоне, который приходит и делает, как привык в другом языке, обсосана миллион раз. Читайте доки, проблемы там описаны. В обычном туториале на оф сайте, кстати, есть описание проблемы с дефолтным значением. Но никто же не хочет ничего читать, правда?
Не осилил (не захотел) понять, как работает замыкание-лябда в питоне - не используй. Но делать дурацкие глобальные выводы "лучше не использовать вовсе" - это смешно.
Последний абзац - это откровенная ересь. Абсолютное большинство не использует лист компрехеншн баги, как ты показал. И не перелезают на питон 3 тоже не поэтому. Все нормальные конторы/сообщества сидят на том, что используют, пока есть возможность. Нахрена куда-то мигрировать, тратить ресурсы, если все и так зашибись? Пока питон 3 не предоставит убедительных преимуществ для тех, кто считает деньги, или пока окончательно не закончится поддержка версии 2.х, полной миграции и не будет. Я уже молчу про то, что версия 3.0 была серьезно перелопачена, а 2.х эволюционно правилась дохрена лет, и вопрос о надежности существует.

Maurog

Читайте доки, проблемы там описаны.
проблем быть не должно и на это рассчитывают наивные новички :grin:

yroslavasako

2. Прежде чем использовать что-то, стоит почитать, как это работает. Проблема новичка в питоне, который приходит и делает, как привык в другом языке, обсосана миллион раз. Читайте доки, проблемы там описаны. В обычном туториале на оф сайте, кстати, есть описание проблемы с дефолтным значением. Но никто же не хочет ничего читать, правда?
Не осилил (не захотел) понять, как работает замыкание-лябда в питоне - не используй. Но делать дурацкие глобальные выводы "лучше не использовать вовсе" - это смешно.
Во-первых, доки большие, и этот тред создан специально для того чтобы рассказать новичкам о таких багах.
Во-вторых, потому я и рассказал про весёлости с лист компрехеншином. И между прочим о надвидимости переменных лисп компрехешина я ничего не нашёл на оффсайте. И более того большая часть питонокодеров, которым я показывал этот трюк с z, сильно удивлялись и начинали отчаянно скрести в затылках, пытаясь вспомнить, не писали ли они когда код, который может попасться на этот баг.
В-третьих, с дефолтным значением - это не проблема, куча туториалов рекомендует этот хак с мутабл коллекцими как способ задания статических переменных в функциях.
В-четвёртых, где я хоть слово писал про лямбду? Ты замыкание от лямбды отличить умеешь? Все проекты, существенно использующие замыкания, написаны на стеклесс версиях питона.
В-пятых, я не жаловался на такую особенность лямбда выражений в питоне. Динамическое связывание - это не баг, это фича. Оно и в первых версиях Великого Лиспа было. Я просто честно предупредил. Сразу сказал, крупным планом, потому что на мой взгляд это знание заслуживает того, чтобы о нём узнали сразу, а не нашли случайно в доках.
Вот заодно ещё один хак (работает для новых классов, с неопределённым свойством __slots__)

def become(a,b):
a.__dict__ = b.__dict__
a.__class__ = b.__class__

VitMix


> python
Python 2.5.2 (r252:60911, Jun 23 2008, 09:16:18)
[GCC 3.4.6 [FreeBSD] 20060305] on freebsd6
Type "help", "copyright", "credits" or "license" for more information.
>>> f=lambda x="8<:477\02092020162\020\037",y="01001000110100101":reduce(lambda x,y:x+y,map(lambda y,x:chr(ord(y)*2+xx,map(int,y;print f;
python readable ?
>>>

pitrik2

не понимаю смысла топика
такого рода страниц в инете выше крыши
конкретно твой пример на каждом углу встречается
уж лучше какиенить красивые решения хитрых задач
хотя опять же в инете полно решений
хотя опять же
вона я в соседнем треде спрашивал как сделать генератор начинающийся не сначала
дык не подсказал никто

tipnote

>Во-первых, доки большие, и этот тред создан специально для того чтобы рассказать новичкам о таких багах.
Они еще и структурированы, чтобы такие отмазки не прокатывали.
>В-третьих, с дефолтным значением - это не проблема, куча туториалов рекомендует этот хак с мутабл коллекцими как способ задания статических переменных в функциях
Ссылку на что-то официальное. Рекомендации дяди Васи меня не устроят.
>В-четвёртых, где я хоть слово писал про лямбду?
Первая ссылка в твоем посте о чем по-твоему?
>Ты замыкание от лямбды отличить умеешь?
Ты не можешь построить замыкание на лямбде?
>Все проекты, существенно использующие замыкания, написаны на стеклесс версиях питона.
Приехали, мама дорогая. Декораторы, по-твоему, как пишутся?
>В-пятых, я не жаловался
"Лучше вообще замыкания не юзать, наверное"
>Вот заодно ещё один хак (работает для новых классов, с неопределённым свойством __slots__)
Мля, теперь сюда еще и разнообразные хаки будут писать? Чтобы новички плодили тоннами говнокод?

july


как сделать генератор начинающийся не сначала
Что ты имеешь в виду?

yroslavasako

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

pitrik2

Что ты имеешь в виду?
там в топике читай после P.S.

psihodog

генератор начинающийся не сначала
так у тебя там проблема не в том, чтобы сделать генератор, начинающийся не сначала, а в том чтобы установить нормально начальное состояние.
причём тут язык? напиши что ты хочешь на руби или на другом языке, тогда понятно будет.
а так, пожалуйста, вот тебе генератор, начинающийся не сначала:
def count(start=0, step=1):
# count(10) --> 10 11 12 13 14 ...
# count(2.5, 0.5) -> 3.5 3.0 4.5 ...
n = start
while True:
yield n
n += step

july

Не, ну естественно, ты создаешь product *всех* букв и цифр, а потом ходишь по нему. В этом случае только и можно, что пропустить n первых элементов.
Неясно, кстати, раз уж ты пользуешься itertools, то почему не взял и dropwhile, а фактически переопределяешь его в next.
Нужно по-другому определить переход к следующей строке, не через product. Тогда можно начинать с любой.

july

Например, вот:

def asciiinc(c):
x = ord(c)
if x >= ord('Z'):
x = ord('A')-1
return chr(x + 1)

def chariter(start='A'):
n = start
while True:
yield n
if n == 'Z': break
n = asciiinc(n)

def aaaiter(start = 'AAA'):
v1,v2,v3 = start
for s1 in chariter(v1):
for s2 in chariter(v2):
for s3 in chariter(v3):
yield(s1+s2+s3)
v3 = 'A'
v2 = 'A'

def alphanumiter(start = "AAA0000"):
alpha = start[:3]
num = string.atoi(start[3:])
for s in aaaiter(alpha):
for n in xrange(num,10000):
yield s + "%04d"%n
num = 0

Скорее всего, можно как-то упростить.

pitrik2

Неясно, кстати, раз уж ты пользуешься itertools, то почему не взял и dropwhile, а фактически переопределяешь его в next.
dropwhile нет в хаскеле :)
P.S.
тьфу, ступил
я не понял про что речь

pitrik2

причём тут язык? напиши что ты хочешь на руби или на другом языке, тогда понятно будет.
я ж написал
в руби это встроено в язык
"lala".next
:)

july

А как он понимает, что часть с "AAA" нужно гонять только по буквам, а часть с "0000" — только по цифрам?

alfadred

dropwhile нет в хаскеле
есть же

pitrik2

о
может я в этом треде буду спрашивать как чтонить сделать?
как вот например tail -f сделать?
понятно что я могу это in-place сделать прям в коде
но хочется тчоб это была некая утилита которую можно реюзать
простейший вариант с генератором:

def tail_f(file, interval=1.0):
while True:
where = file.tell
line = file.readline
if not line:
time.sleep(interval)
file.seek(where)
else:
yield line

проблема в том что непонятно как это остановить?
g.next заснет же мертвым сном если в файле долго ничего не будет...
хочет в этот next таймаут чтоли передавать

psihodog

в руби это встроено в язык
"lala".next
так это тебе нужен не
генератор начинающийся не сначала
, а функция по строке выдающая "следующую" строку.

Maurog

dropwhile нет в хаскеле
нуууу

SCIF32

а нельзя таймаут в генератор передавать?
если таймаут исчерпан, то он отваливается.

yroslavasako

ты dropwhile от dropWhile отличаешь? а ghc - отличает

yroslavasako

а давайте не будем из генераторов делать микротреды? Либо заюзаем стандартные питоновские средства - PyPy, stackless haskell, либо и вовсе перейдём на использование go каналов?

tipnote

Тогда давай определяться, что такое полноценное замыкание, а что нет. А то в инете у всех свое мнение, потому что классическому, вроде как, даже nested functions в питоне подходят.
>А чем тебе become не понравился? Вполне удобный способ подменить объект, сохранив все ссылки на него
Во-первых, не подменить, так как при добавлении атрибута у одного, он автоматически добавиться к другому.
Во-вторых, зачем такие хаки вместо copy.deepcopy?
В-третьих, приведи пример конкретной задачи, где это используется? То есть, зачем для редкой задачи генерить неявный хак, когда можно длиннее, но понятней и без хаков написать?
В-четвертых, зачем новичкам эта жуть?

pitrik2

а нельзя таймаут в генератор передавать?
если таймаут исчерпан, то он отваливается.
как-то некрасиво...
тогда уж передавать функцию, которая если false вернет то перестать работать

conv3rsje

g.next заснет же мертвым сном если в файле долго ничего не будет...
ну так ты определись, что ты хочешь от этой функции?
чтоб блокировалась или нет?

SCIF32

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

pitrik2

ну так ты определись, что ты хочешь от этой функции?
чтоб блокировалась или нет?
я хочу чтобы блокировалась
но при этом чтобы у программы была возможность завершиться не только по ctrl+c
или вы предлагаете поток который вычитывает файл просто убивать?
P.S.
как кстати в питоне ctrl+c отловить?
верней нужно не отловить а грамотно завершить работу проги в случае убивания

yroslavasako

1. Замыкание - объект-функция, связанная с некоторым контекстом выполнения. Для стабильного приминения желательно, чтобы контекст оставался тот, в котором эту функцию создавали. В моём примере с этим проблема, переменная цикла, на которую опирались функции, пробежала цикл и стала для всех замыканий одинаковой. Проблема решается явным заявлением использованных внешних переменных и сохранением их во вновь созданном контексте (поскольку он только что созданный - его менять некому кроме возвращённой функции). Я этот контекст создал, дважды применив лямбду, для внутренней лямбды контекст уже отделился от родительского и потому не был измененён. Такой подход прокатывает, пока количество переменных в контексте, на которое опирается замыкание невысоко. Если их много или их трудно выделить для явного сохранения, то мы имеем шансы получить по носу в самых неожиданных местах. Потому и родилась идея хранить вместе с кодом его контекст - stackless python, переложив эти заботы на плечи интерпретатора, предоставляющего дополнительное API для обсуждаемых действий.
2. Очень простой пример. У нас есть матрица, интерфейс для работы с ней. И мы сделали два класса - для плотных и разряженных матриц. В определённый момент плотная матрица (пересчитав себя в бекграунде) обнаруживает, что она не такая уж и плотная и подсовывает вместо себя разряженную, собой инициализированную. Можно конечно задействовать включение, сделать ещё один класс proxy, который будет вызывать для каждой функции интерфейса соответствующую функцию для self.matrix, но тогда это отображение функций нужно делать руками. А так мы создаём MatrixProxy, наследником AbstractMatrix, и в нужным местах подставляем вместо себя инстанс либо SparceMatrix, либо DenseMatrix

tipnote

P.S.
как кстати в питоне ctrl+c отловить?
верней нужно не отловить а грамотно завершить работу проги в случае убивания
http://docs.python.org/library/signal.html

pitrik2

ну если интервал передавать, то и таймаут не сильно портит по-моему.
блин
таймаут нафик не нужен
сделал пока так:

def tail_f(file, interval=1.0, shouldStopReadingF=lambda: False):
while True:
if shouldStopReadingF:
return
where = file.tell
line = file.readline
if not line:
time.sleep(interval)
file.seek(where)
else:
yield line


def _readLogFile(filename, callback, interval, shouldStopReadingF):
file = open(filename, 'r')
file.seek(0, 2) # go to end of file
lines = utils.tail_f(file, interval=interval, shouldStopReadingF=shouldStopReadingF)
try:
for line in lines:
callback.onNextLine(line)
except StopIteration:
pass
finally:
file.close


class Application(object):
def __init__(self, logfile):
self.logfile = logfile
self.reader = None

def onNextLine(self, line):
print line

def start(self):
self.isStopped = False
self.reader = threading.Thread(target=_readLogFile, args=(self.logfile, self, 1, lambda: self.isStopped
self.reader.start

def stop(self):
self.isStopped = True
if self.reader:
self.reader.join

a = Application('lala')
a.start
time.sleep(10)
print 'stopping'
a.stop
print 'stopped'
time.sleep(20)

вот кстати
в рамках этого треда подскажите
можно ли в качестве дефолтного аргумента указывать лямду?
можно ли лямду ссылающуюся на self передавать чужому потоку?

conv3rsje

> я хочу чтобы блокировалась
то есть для этого генератора будет отдельная нитка?
> но при этом чтобы у программы была возможность завершиться не только по ctrl+c
ну да, timeout, функция или флаг
вроде другого ничего не придумали
> или вы предлагаете поток который вычитывает файл просто убивать?
мы ничего не предлагаем, мы дико тупим и пытаемся понять что вообще нужно :D
PS Реализация с readline плохая, может разбить одну строчку посередине.
PPS Я бы всё же использовал poll + read вместо sleep + readline

conv3rsje

> можно ли в качестве дефолтного аргумента указывать лямду?
можно, в чем проблема?
> можно ли лямду ссылающуюся на self передавать чужому потоку?
аналогично, пусть насяльника меня поправит, но вроде как лямбда
это почти обычная функция, только анонимная
upd

while True:
if shouldStopReadingF:
return

Ужас!

while not shouldStopReadingF:
...

tipnote

2. Очень простой пример. У нас есть матрица, интерфейс для работы с ней. И мы сделали два класса - для плотных и разряженных матриц. В определённый момент плотная матрица (пересчитав себя в бекграунде) обнаруживает, что она не такая уж и плотная и подсовывает вместо себя разряженную, собой инициализированную. Можно конечно задействовать включение, сделать ещё один класс proxy, который будет вызывать для каждой функции интерфейса соответствующую функцию для self.matrix, но тогда это отображение функций нужно делать руками. А так мы создаём MatrixProxy, наследником AbstractMatrix, и в нужным местах подставляем вместо себя инстанс либо SparceMatrix, либо DenseMatrix
У меня, честно говоря, возникает ощущение, что либо я не осознал задачу, либо архитектура решения хреновая.
момент первый: почему матрицы сами себя подменяют вместо того, чтобы отдать управление чехардой третьему лицу
момент второй: почему класс полной матрицы проверяет себя на полноту
я увидел задачу так (поправь): в системе появляются данные, по ним определяется, какую матрицу использовать, матрица используется - замыкаем цикл

tipnote

но вроде как лямбда
это почти обычная функция, только анонимная
лямбда в питоне - это синтаксический сахар. обычная функция с синтаксическими ограничениями на тело

yroslavasako

задача - сделать эффективное представление матрицы. Возможно выполнение функции normalize в фонов режиме (таким же образом, как и работа garbage collectorа либо просто в момент операции определяется что делать с результатом: конвертировать или нет (посчитать во время умножения количество ненулевых элементов не усложняет задачу умножения хранить одну версию представления или обе.

conv3rsje

задача - сделать эффективное представление матрицы. Возможно выполнение функции normalize в фонов режиме
Как бы тут противоречие...
При "эффективном" представлении, решении или еще чего там у тебя просто _нет_ фонового режима.

stm6692945

кому нахуй питон нужен
пишите на жабе

Dasar

> момент первый: почему матрицы сами себя подменяют вместо того, чтобы отдать управление чехардой третьему лицу
> момент второй: почему класс полной матрицы проверяет себя на полноту
> я увидел задачу так (поправь): в системе появляются данные, по ним определяется, какую матрицу использовать, матрица используется - замыкаем цикл
если сделать так как ты говоришь, то как будет выглядеть в коде, например, сложение двух полных матриц, в результате которых получается разреженная матрица?
в решении код будет аля matrix1 += matrix2; и в результате matrix1 автоматически становится разреженной.

conv3rsje

в решении код будет аля matrix1 += matrix2; и в результате matrix1 автоматически становится разреженной.
опустим вопрос про копирования, но вспомним, что перевод в/из разреженного формата весьма не бесплатен
и если у тебя более одной операции матричной то на этом можно погореть, прыгая постоянно из одного в другой формат

Dasar

>и если у тебя более одной операции матричной то на этом можно погореть, прыгая постоянно из одного в другой формат
вопрос быстрого определения когда действительно необходимо перепрыгнуть тоже стоит опустить, потому что он не имеет отношения к поднятому ранее вопросу

conv3rsje

определить быстро - совсем не проблема
проблема - это рюхнуть что конверсия действительно _нужна_
а это может решить только пользователь, который знает, что он там будет делать потом
это я к тому что пример дурацкий :)

Dasar

не нашел отличий в твоей фразе от моей
>>проблема - это рюхнуть что конверсия действительно _нужна_
>dg>вопрос быстрого определения когда действительно необходимо перепрыгнуть тоже стоит

yroslavasako

вопрос быстрого определения когда действительно необходимо перепрыгнуть тоже стоит опустить, потому что он не имеет отношения к поднятому ранее вопросу
значит можно ещё ввести класс DelayedMatrix

Serab

У меня, честно говоря, возникает ощущение, что либо я не осознал задачу, либо архитектура решения хреновая.
момент первый: почему матрицы сами себя подменяют вместо того, чтобы отдать управление чехардой третьему лицу
момент второй: почему класс полной матрицы проверяет себя на полноту
я увидел задачу так (поправь): в системе появляются данные, по ним определяется, какую матрицу использовать, матрица используется - замыкаем цикл
да к черту матрицы, он про шаблон State.
Фишка вот в чем: он по сути сказал, что шаблон State реализуется на питоне гораздо лаконичнее, чем на C++. Таких примеров для разных языков и шаблонов довольно много. В пример привел применение шаблона State к матрицам. Все замечания тут относятся к адекватности применения этого шаблона к этой задаче, но не к питону и его преимуществах, State — довольно часто используемый шаблон, лаконичность его реализации — это аргумент.

Serab

Да даже еще сильнее: State в питоне вообще не нужен по сути, там эта функциональность доступна напрямую.

apl13

значит можно ещё ввести класс DelayedMatrix
Lazy.

SCIF32

LazyMatrix и
Оставить комментарий
Имя или ник:
Комментарий: