[о боже какой говнокод] Вопрос знатокам ФЯ и/или питона [решена]

Serab

Идея: есть функция от скажем двух параметров. Я хочу один параметр задать в одном месте, а другой задавать позже.
В моем конкретном случае это функция, которая как-то разбирает внутренности файла и вычленяет оттуда некоторую информацию. Пример: regex_exctactor ищет заданный регэксп. Есть и другие, но первым параметром у них всегда файловый объект. Так вот этот параметр файловый объект задается где-то внутри фрэймворка, а юзер по сути должен фрэймворку передать функцию от единственного параметра. Надеюсь, понятно.
Вопрос первый: как то, что я хочу, называется в теории ФЯ?
Нахуярил такой говнокод:

def decor(func):
def get(*args):
def wrap(file):
return func(file, *args)

return wrap

return get

@decor
def regex_extractor(f, regex):
if isinstance(regex, basestring):
regex = re.compile(regex)

f.seek(0)
for line in f:
match = regex.search(line)
if match:
return " ".join(match.groups
else:
return "<NF>"

@decor
def a_bit_more_complicated_extractor(f, param1, param2):
pass

time_extractor = regex_extractor("Program successful\. (\d+) secs.")
some_shit_extractor = a_bit_more_complicated_extractor(42, 42)

Вопрос второй: Как это менее говнокодно выразить в питоне?

pilot

Вопрос второй: Как это менее говнокодно выразить в питоне?
rtfm

6yrop

Вопрос первый: как то, что я хочу, называется в теории ФЯ?
наверное вот это http://en.wikipedia.org/wiki/Currying

Serab

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

Serab

Пока так:

def decor(func):
return lambda *args: partial(func, *args)

@decor
def regex_extractor(regex, f):

Вот такой декор позволяет отрезать любое количество параметров с начала. Это более лакончино, но я теперь не могу сказать, что в какой-то отдельный момент времени понимаю все детали работы этого дела :ooo:

Serab

хотя не, вот сейчас понимаю :grin:

Serab

вот, спасибо, так гораздо больше по делу ищется, binding — не то, конечно.

apl13

В хаскеле — сечение (section).
В C++ — std::bind.

asvsergey

А просто использовать лямбду нельзя?
  
file = open(...)
f = lambda x : regex_extractor(file, x)
...
f('some text')

apl13

Я хочу один параметр задать в одном месте, а другой задавать позже.
Поправь, что ли, "один" на "первый", так?

Serab

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

apl13

А, ну тогда это не сечение.
Вернее, для второго аргумента это сечение flip f.
flip :: (a -> b -> c) -> (b -> a -> c)
flip f x y = f y x

Maurog

Вернее
неверно, сечение это лишь синтаксический сахар для построения замыканий для операторов
http://www.haskell.org/haskellwiki/Section_of_an_infix_opera...
то, что ты описываешь лучше назвать "частичное применение", которое актуально как раз для каррированных функций

apl13

неверно, сечение это лишь синтаксический сахар для построения замыканий для операторов
http://www.haskell.org/haskellwiki/Section_of_an_infix_opera...
то, что ты описываешь лучше назвать "частичное применение", которое актуально как раз для каррированных функций
Объясни мне разницу, а?

Serab

Вообще, вот как не выебываясь это делать, вроде :)
Утром вечером удренеет ©

def regex_extractor(regex):
def wrap(f):
if isinstance(regex, basestring):
regex = re.compile(regex)

f.seek(0)
for line in f:
match = regex.search(line)
if match:
return " ".join(match.groups
else:
return "<NF>"

return wrap

beluchy

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

Serab

Все-таки насколько я понял, в питоне все достаточно хорошо, чтобы в таких случаях не приходилось функторы городить.
И еще раз, я пишу либу. Юзеры ее вызывает в духе collect_data([regex_extractor("Time: (\d+) seconds" something_else_extractor("Hello, wordl", 42)]). Т.е. collect_data принимает список функций с одним параметром-файловым объектом (и потом уже внутри либы они прогоняются для набора файлов). Да, можно передать callable-объект, но это буэшечка же вроде.

beluchy

Юзеры ее вызывает в духе collect_data([regex_extractor("Time: (\d+) seconds" something_else_extractor("Hello, wordl", 42)]). Т.е. collect_data принимает список функций с одним параметром-файловым объектом (и потом уже внутри либы они прогоняются для набора файлов).
в твоем вызове collect_data принимает список значений, возвращаемых функциями.
Почему ты предпочел такой интерфейс, казалось бы более естественному:

obj = myClass(file)
obj.regex_extractor("Time: (\d+) seconds") # save extracted data in object
obj.something_else_extractor("Hello, wordl", 42) # save extracted data in object
obj.collect_data #process saved data?

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

Anturag

мне кажется неявное участие во всех этих вызовах где-то когда-то открытого файла это как раз то, с чем пытается бороться функциональное программирование
С каких пор partial application в функциональном программировании под запретом? :confused:

Serab

и как тут с расширяемостью, мне надо все эти методы в один класс добавлять? И что они будут делать внутри? Они должны как-то добавить информацию о действиях в будущем в ходе вызова collect_data. Короче, вообще не понял я этот интерфейс и как ты его собирался реализовать, у меня же все предельно просто.

def collect_data(funcs):
for filename in filenames:
with open(filename) as f:
print " ".join(func(f) for func in funcs)

и вообще
obj = myClass(file)
я же написал даже с примерами, что файлов несколько и какой именно файл юзеру не известно, он просто задает действия, которые с ними надо выполнить.

Alena_08_11

obj = myClass(file)
я же написал даже с примерами, что файлов несколько и какой именно файл юзеру не известно, он просто задает действия, которые с ними надо выполнить.
Ну тут скажем, можно инстанцировать фабричным методом.

obj = my_framework.give_me_my_regexp_extractor
...

Но в целом это не по змеячи.
Где то читал статью недавно, про некую либу на python, которую делала какая то контора и в которой были тыщи строк кода и сотни классов.
Чуваку, который это использовал, надоело что всё ломается и глючит с каждой новой версией либы, он стал пинать чуваков из конторы и они совместными усилиями избавились от классов вообще и осталось чё то типа полсотни строк кода ...
Посыл такой, что подход, предложенный форумчанином с ником из цифр в конце концов приведёт к "тысячам строк кода и сотням классов".

beluchy

collect_data([regex_extractor("Time: (\d+) seconds" something_else_extractor("Hello, wordl", 42)])
def collect_data(funcs):
for filename in filenames:
with open(filename) as f:
print " ".join(func(f) for func in funcs)
Да, можно передать callable-объект, но это буэшечка же вроде.
мне кажется, или кто-то путается в показаниях?

apl13

мне кажется неявное участие во всех этих вызовах где-то когда-то открытого файла это как раз то, с чем пытается бороться функциональное программирование.
"Хранящийся в функции" — это не "где-то когда-то открытый". Это вполне себе stateless. Состояние файлового объекта хранится явно, в передаваемом значении.
.

Serab

мне кажется, или кто-то путается в показаниях?
Мне кажется, кто-то путается в нити обсуждения.
Я имел в виду класс с написанным вручную методом __call__.

beluchy

Я имел в виду класс с написанным вручную методом __call__
Да, можно передать callable-объект, но это буэшечка же вроде.
и кстати, почему это буэшечка?

beluchy

Это вполне себе stateless
В референсной реализации топикстартера - еще как statefull, а благодаря декоратору догадаться об этом можно, разве что если вам удалось догадаться про "класс с написанным вручную методом __call__"

Serab

ну посмотри, мой код я весь уже запостил в треде, это пара строк и все ясно:

from functools import wraps

def regex_extractor(regex):
@wraps(regex_extractor)
def wrap(f):
# code goes here
pass

return wrap

def other_extractor(some, parameters):
@wraps(other_extractor)
def wrap(f):
# code goes here
pass

return wrap

def collect_data(funcs):
for filename in filenames:
with open(filename) as f:
print " ".join(func(f) for func in funcs)

collect_data([regex_extractor("foo (bar)" regex_extractor("(foo) bar" other_extractor("foo", "bar")])

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

Serab

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

apl13

В референсной реализации топикстартера - еще как stateful
А, ну нечетал. :o

pilot

мне кажется, или кто-то путается в показаниях?
Java-архтекторы набижали.

beluchy

from functools import wraps
def regex_extractor(regex):
    @wraps(regex_exctractor)
    def wrap(f):
     # code goes here
     pass
    
    return wrap
regex_extractor("foo (bar)")
ты это запускал ; )

Serab

нет, конечно, видно же, что для демонстрации. Ну опечатался на буквочку, ну и что? Вон там вообще используеются filenames, которому ничего не присвоено. Короче тупой троллинг какой-то :(

beluchy

Ну опечатался на буквочку
from functools import wraps
def regex_extractor(regex):
@wraps(regex_exctractor)
def wrap(f):
# code goes here
pass
return wrap
Короче тупой троллинг какой-то
мне интересно было понять почему ты выбрал такое решение. я не встречал такого в интерфейсах.
ЗЫ ну так и почему передавать callable - буэшечка?

Serab

def regex_extractor(regex):
    def wrap(f):
     # code goes here
     pass
  return wrap
... чувак, пробелами меня на форуме еще не подъебывали :) Причем у меня все правильно в посте, я не знаю, как ты это копируешь.
ЗЫ ну так и почему передавать callable - буэшечка?
передавать его окейно, но буэшечно в таком языке, как питон, с кложурами, создавать класс для решения моей задачи.

beluchy

пробелами меня на форуме еще не подъебывали
блин, я тебя не пробелами подъебываю, а тем что там декоратор не нужен и не работает, зато смущает реально и я его зачеркнул.
вообще сознаюсь, я как-то пропустил мимо сознания твою утреннюю версию - она окейная.
С другой стороны я до сих пор не видел программных интерфейсов организованных таким образом. В твоем случае, если пользователь задумает дополнить твою либу еще каким экстрактором ему придется писать неочевидным образом устроенную функцию, которая принимает в качестве аргументов все кроме файла и возвращает функцию которая уже ест только файл.
Обычно в тех api что я видел в такой ситуации передается собственно функция которая ест все аргументы и с ней список (дополнительных) аргументов к ней.

Serab

Обычно в тех api что я видел в такой ситуации передается собственно функция которая ест все аргументы и с ней список (дополнительных) аргументов к ней.
ну, кстати, да. Просто меня занял именно такой путь рассуждения, как в первом посте. Да, юзерам надо показывать пример. Ну да, можно параметры и отдельно.

soroka000

[offtop] Мне кажется или питон набирает популярность на флокале? [/offtop]

yroslavasako

Мне кажется или питон набирает популярность на флокале?
Да хз. Я уже давно им пользуюсь, потому что писать скрипты на баше мне нравится меньше чем на питоне. Ну гуи рисовал по-быстрому на питоне+Qt. А серьёзно питоном пользуются только веб-программисты, где он всё же удобнее пыхпыха. Других областей применения питона в больших разработках я не встречал. А если пишут на нём по большей части скриптики, то что там обсуждать?

bleyman

Разнообразные учёные очень любят использовать Питон ещё для всяких вычислений, и выбивают под его развитие огромные количества бабла, судя по продвинутости numpy, SciPy, Sage, IPython, Cython, и прочих взаимноопыляющих проектов. Хотя до России это новомодное веяние могло ещё не дойти!

Serab

я даже на симпи нафигачил программку полгода назад :)
Оставить комментарий
Имя или ник:
Комментарий: