Как сопоставить функцию со всеми значениями аргументов в виде списка? но иметь явные имена аргументов в определении функции

Я хочу определить функцию, используя явные имена аргументов ff(a,b,c) в определении функции, но я также хочу сопоставить функцию со всеми аргументами, чтобы получить список:

ff(a,b,c):
    return list(map(myfunc,[a,b,c]))

Однако я не хочу явно писать имена параметров внутри функции как a, b, c. Я хочу сделать это как

ff(a,b,c):
    return list(map(myfunc,getArgValueList()))

getArgValueList() извлечет значения аргументов по порядку и сформирует список. Как это сделать? Есть ли встроенная функция, такая как getArgValueList()?


person user15964    schedule 14.05.2018    source источник
comment
Используйте *args в качестве параметра.   -  person Code-Apprentice    schedule 14.05.2018
comment
Вы не можете взять аргументы с явными a, b, c параметрами, но затем использовать их как args. Вы делаете то или иное. Конечно, вы всегда можете написать args = a, b, c в качестве первой строки функции, но я не уверен, что это вам даст. Возможно, если бы мы знали, почему вам это нужно, а не бессмысленную игрушечную задачку, мы могли бы объяснить что-то лучше. Но то, о чем вы просите, невозможно.   -  person abarnert    schedule 14.05.2018
comment
Ну, не невозможно, если вы не возражаете против некоторых уродливых отражений и/или фрейм-хаков. Который я был бы рад объяснить, если вы действительно хотите. Но это не очень хорошая идея, если ваша цель — сделать ваш код более читабельным, элегантным, эффективным… Единственный случай, когда это может быть полезно, — это когда вы динамически генерируете функции и вам нужна большая гибкость.   -  person abarnert    schedule 14.05.2018
comment
@abarnert Я думаю, что ОП хочет, чтобы приведенный ниже ответ был изменен с учетом обоих наших комментариев.   -  person Code-Apprentice    schedule 14.05.2018
comment
@Code-Apprentice ОП сказал, что я хочу, чтобы интерфейс аргумента был явным. Это явное написание имен аргументов как ff(a,b,c) при создании определения функции и даже отредактированный вопрос, чтобы выделить его жирным шрифтом после того, как вы сделали свои комментарии к ответу, так что ... я думаю, даже если это то, что OP должен хочу, это не то, что хочет ОП.   -  person abarnert    schedule 14.05.2018
comment
@abarnert @Code-Apprentice Большое спасибо за комментарий. То, что я хочу, просто, мне просто нужна функция getArgValueList(), которую можно использовать внутри другой функции (имя аргумента явное), чтобы получить значение аргумента в виде списка   -  person user15964    schedule 14.05.2018
comment
@abarnert Ничего себе ... Я только что видел отредактированный вопрос.   -  person Code-Apprentice    schedule 14.05.2018
comment
@Code-Apprentice Да, я думаю, что Абарнерт меня правильно понял :)   -  person user15964    schedule 14.05.2018
comment
@user15964 user15964 Это сложно сделать, как сказал выше Абарнерт. Вы можете явно передать аргументы в вызове функции, но вы должны объявить функцию как def ff(*args). С другой стороны, если вы используете **kwargs, вы можете очень близко подойти к тому, о чем просите. Я предлагаю вам прочитать ссылку над вашим вопросом для более подробной информации.   -  person Code-Apprentice    schedule 14.05.2018
comment
@Code-Apprentice Хорошо, я прочитаю. Большое спасибо   -  person user15964    schedule 14.05.2018
comment
Можете ли вы объяснить, почему вам нужна функция getArgValueList, или привести более реалистичный пример того, что вы собираетесь с ней делать? Что бы вы ни пытались сделать, вероятно, есть лучший способ сделать это, даже если это не *args.   -  person abarnert    schedule 14.05.2018
comment
Возможный дубликат преобразования списка в *args при вызове функции   -  person smci    schedule 14.05.2018
comment
@smci Как это могло быть дубликатом? У ОП нет списка здесь.   -  person abarnert    schedule 14.05.2018
comment
@abamert: у OP есть список здесь, если они используют синтаксис * args, о чем они просили. Они спрашивают, как передать аргументы в виде списка. Это дубликат. Если вы все еще не согласны, опубликуйте, какой из многих других вопросов SO он дублирует.   -  person smci    schedule 14.05.2018
comment
Ах, извините. ОП хочет позвонить f(a), f(b), ... on each of *args. Тот, кого я связал, хочет позвонить f(*args). Но все же это должен быть дубликат, пожалуйста, помогите определить, какой именно.   -  person smci    schedule 14.05.2018
comment
@smci Я не думаю, что это дубликат. Что хочет сделать OP, так это определить функцию с явными параметрами, но затем все равно получить список аргументов. Это необычная вещь, поэтому она не будет соответствовать очевидным дупликациям с использованием varargs или splats, даже если на первый взгляд это выглядит так.   -  person abarnert    schedule 14.05.2018
comment
FWIW, при написании Python рекомендуется использовать обычные идиомы Python, не пытайтесь писать Java на Python.   -  person PM 2Ring    schedule 14.05.2018
comment
Почему вы хотите иметь явные имена аргументов в определении функции? Если у вас есть уважительная причина, то какая? Что не так с внутри тела функции, снова распаковкой позиционных аргументов в именованные переменные a = args[0], b = args[1]...   -  person smci    schedule 14.05.2018
comment
@ PM2Ring Это еще менее идиоматическая Java, чем Python. Это… может быть (до ES5) JavaScript, но на самом деле он больше похож на Perl, чем на что-либо другое.   -  person abarnert    schedule 14.05.2018
comment
@abarnert Хорошо. Я просто предположил, что это Java-вещь, в основном из имени функции camelCase (я не пишу Java).   -  person PM 2Ring    schedule 14.05.2018


Ответы (2)


То, что вы пытаетесь сделать, невозможно без уродливых хаков. Вы либо берете *args и получаете последовательность значений параметров, которые можно использовать как args:

def ff(*args):
    return list(map(myfunc, args))

… или вы берете три явных параметра и используете их по имени:

def ff(a, b, c):
    return list(map(myfunc, (a, b, c)))

…но либо то, либо другое, а не то и другое.

Конечно, вы можете поместить эти значения в последовательность самостоятельно, если хотите:

def ff(a, b, c):
    args = a, b, c
    return list(map(myfunc, args))

… но я не уверен, что это покупает вас.


Если вы действительно хотите знать, как написать функцию getArgValueList, я объясню, как это сделать. Однако, если вы хотите сделать свой код более читабельным, более эффективным, более идиоматичным, более понятным, более кратким или почти что-то еще, это будет иметь прямо противоположный эффект. Единственная причина, по которой я мог себе представить, чтобы сделать что-то подобное, — это если бы вам нужно было динамически генерировать функции или что-то в этом роде — и даже тогда я не могу придумать причину, по которой вы не могли бы просто использовать *args. Но если вы настаиваете:

def getArgValueList():
    frame = inspect.currentframe().f_back
    code = frame.f_code
    vars = code.co_varnames[:code.co_argcount]
    return [frame.f_locals[var] for var in vars]

Если вы хотите узнать, как это работает, большая часть информации находится в модуле inspect. документы:

  • currentframe() получает текущий кадр — кадр getArgValueList.
  • f_back получает родительский фрейм — фрейм того, кто вызвал getArgValueList.
  • f_code получает объект кода, скомпилированный из тела функции того, кто вызвал getArgValueList.
  • co_varnames — это список всех локальных переменных в этом теле, начиная с параметров.
  • co_argcount — это количество явных параметров позиции или ключевого слова.
  • f_locals — это словарь с копией окружения locals() фрейма.

Это, конечно, работает только для функции, которая не принимает *args, аргументы только с ключевыми словами или **kwargs, но вы можете расширить ее, чтобы она работала и для них, немного поработав. (Подробнее см. co_kwonlyargcount, co_flags, CO_VARARGS и CO_VARKEYWORDS.)

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

person abarnert    schedule 14.05.2018
comment
Привет, @abarnert. Большое спасибо, что уделили так много времени ответу. Я многому научился. Но похоже, что getArgValueList работает неправильно. getArgValueList(1,2,3) дает (), если я определил его как def getArgValueList(a,b,c): - person user15964; 14.05.2018
comment
@user15964 user15964 Почему вы определили его как getArgValueList(a,b,c). Вам нужна функция, которую можно вызывать как getArgValueList() из любой функции. Вот что я тебе дал. И это работает. Если случайно изменить его на что-то другое, не понимая, что это не работает, это не удивительно. - person abarnert; 14.05.2018
comment
О, мне очень жаль. Мой мозг должен был столкнуться с хаосом ранее. Это действительно работает. Большое спасибо, и я решил принять ваш ответ, потому что вы предоставляете явное getArgValueList. Хотя решение jferard также работает, кажется, что построить getArgValueList с помощью его подхода непросто. - person user15964; 14.05.2018
comment
@user15964 user15964 Да, Python позволяет возможность делать всевозможные умные вещи, но те, которые обычно слишком умны, чтобы быть хорошей идеей, обычно делают немного неприятными. - person abarnert; 14.05.2018

*args конструкция предоставит вам аргументы в виде список:

>>> def f(*args): return list(map(lambda x:x+1, args))
>>> f(1,2,3)
[2, 3, 4]

Если вы привязаны к подписи f, вам придется использовать модуль inspect:

import inspect

def f(a, b,c):
    f_locals = locals()
    values = [f_locals[name] for name in inspect.signature(f).parameters]
    return list(map(lambda x:x+1, values))

inspect.signature(f).parameters дает вам список аргументов в правильном порядке. Значения находятся в locals().

person jferard    schedule 14.05.2018
comment
Разве это не должно быть print(f(1, 2, 3))? - person Code-Apprentice; 14.05.2018
comment
@Code-Apprentice @jferard Большое спасибо. Но на самом деле я хочу, чтобы интерфейс аргумента был явным. Поэтому я должен написать ff(a,b,c) - person user15964; 14.05.2018
comment
@ user15964 Вы тоже можете это сделать. (Обратите внимание, что я не писал этот ответ.) - person Code-Apprentice; 14.05.2018
comment
@Code-Apprentice If должно быть f(1,2,3), но и функция должна быть list(map(lambda x:x+1, args)). - person abarnert; 14.05.2018
comment
@abarnert Хороший улов. - person Code-Apprentice; 14.05.2018
comment
Как написано, эта функция не принимает 3 аргумента, она принимает один аргумент, представляющий собой список из 3 элементов. Если вы хотите это сделать, вам вообще не нужен знак — просто def f(lst): return list(map(lambda x: x+1, lst)). - person abarnert; 14.05.2018
comment
@ user15964 Да, есть. Возьмите ответ, данный здесь, затем используйте мой первый комментарий и первый комментарий abarnert, чтобы исправить его, чтобы делать то, что вы хотите. Для получения более подробной информации прочитайте ответы на вопрос, связанный вверху. - person Code-Apprentice; 14.05.2018
comment
@user15964 user15964 Вы должны писать ff(a,b,c) при вызове функции или при ее определении? - person jferard; 14.05.2018
comment
@user15964 user15964 См. последнюю редакцию этого ответа. Он делает то, что вы хотите? - person Code-Apprentice; 14.05.2018
comment
Зачем использовать getfullargspec вместо signature? Я знаю, что getfullargspec не устарел в 3.6, но только для простоты переноса с 2.7, а не для нового кода. - person abarnert; 14.05.2018
comment
@abarnert Исправлено. Спасибо. - person jferard; 14.05.2018
comment
@jferard Умный трюк! Большое спасибо! Это действительно то, что я хочу +1 - person user15964; 14.05.2018