2015-08-28 3 views
-1

Учитывая функцию, которая принимает **kwargs, я хочу динамически устанавливать значения по умолчанию для этих аргументов ключевого слова. Возможно ли это сделать в Python 2.7 и Python 3?Изменить kwarg defaults

В коде я хочу set_kw_defaults, что:

def f(**kwargs): 
    print kwargs 
    # in practice, much complicated black-box processing happens 
    # here; printing the kwargs is just an example 

set_kw_defaults(f, {'magic': 'is possible'}) 

f() 

печатает:

{'magic': 'is possible'} 

Я знаю, как выполнить этот вид динамического Arg-умолчанию переписывания для позиционных аргументов, и для Python 3/PEP 3102 "только ключевые слова". Я также знаю, как создавать декораторы для обертывания f и перехватывать/модифицировать kwargs. Это не что я хочу здесь. Я хочу установить значения по умолчанию для произвольных kwargs, которые не были задуманы, когда был определен f, и я не могу украсить f. С этими ограничениями возможно set_kw_defaults?

Чтобы уточнить: f не является функцией, для которой я могу изменить исходный код или украсить. К лучшему или худшему, он уже принимает свои аргументы как **kwargs. Это не обычный вопрос, связанный с передачей аргументов, а странный выброс. Я ищу помощь от тех, кто понимает внутренности передачи аргументов Python.

Разъяснение повторно украшения: Я не могу украсить или обернуть f, потому что оформление и упаковка создает новый объект (назовем его f2), который называется в качестве прокси-сервера для оригинального f. Если сделано с @decorator синтаксиса, простой функциональной обертке, или схемы на основе классов, все они сводятся к:

def f2(**kwargs): 
    kwargs.setdefault('color', 'green') 
    f(**kwargs) 

Но если другие части работающей системы уже указывают на f, они не будут затронуты мое создание f2. Если бы это были позиционные аргументы или аргументы kw-only, мне нужно было изменить значения по умолчанию, я мог бы сделать это в исходном объекте f, и все пользователи f получат новые значения по умолчанию. Если я украшу, это не так.

+0

Я не понимаю, что вы имеете в виду при настройке значений по умолчанию для «произвольных кваргов, которые не были задуманы, когда был определен« f »». Если у вас есть аргумент, который вы хотите запомнить (например, ваш '{'magic': 'possible'}'), вы можете написать свою функциональную строку, например 'def f (magic = 'possible', ** kwargs) '. Если нет, то как вы придумываете ключи для своего «dict», вы переходите к гипотетическим «set_kw_defaults»? –

+1

Невозможно это сделать. –

+0

@ Two-BitAlchemist Я считаю, что op хочет установить значение по умолчанию для всех аргументов '** kwargs'. –

ответ

1

Я пришел к выводу, что это невозможно сделать в Python.

Я полагал, что если значения по умолчанию хранятся для обычных позиционных аргументов (а в Python 3 для аргументов только для ключевого слова), то в определениях функций может быть внутренний dict, который хранит эти сопоставления. Этот дикт может быть изменен.

Но, похоже, это не так просто. В Python 2, хотя аргументы со значениями по умолчанию выглядят как значения kwarg, они на самом деле не являются. Значения по умолчанию хранятся в кортеже, который соответствует позиционным аргументам. С разборкой dis.dis я обнаружил, что ссылки на эти позиционные аргументы довольно глубоко встроены в байт-код, даже когда есть значения по умолчанию.

В Python 3 ситуация немного лучше, учитывая, что есть аргументы «только по ключевому слову», а значения по умолчанию действительно хранятся как dict. Этот дикт может быть изменен. Таким образом, это успех успеха. Но изменяющие значения для kwargs, которые не являются предварительно известными как ключевые слова, не работают; эти «дополнительные» значения игнорируются.

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

Несколько респондентов предложили изменить исходный код (не возможно здесь) или украсить функцию/добавив к ней переднего прокси-сервера, который объединяет параметры kwarg. Хотя во многих ситуациях было бы правильно, что я интегрируюсь в сложное существующее приложение, я должен искать ранее существовавшее пространство объектов Python для ссылок на исходную функцию и заменять их ссылками на прокси. Подобный исчерпывающий поиск может быть возможен в некоторых обстоятельствах, но в общем случае он кажется рискованным. Было бы полезно найти некоторые существующие ссылки на f и заменить их ссылками на f2, но я не вижу, как это можно сделать для всех возможных ссылок. Это, вероятно, также потребует обширной памяти. Другим навязчивым подходом может быть динамическая переработка байт-кода. Теоретически это может сработать ... но я не уверен в этом.

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

Спасибо всем за обсуждение и идеи.

+0

Вы сказали, что не можете изменить исходный источник, однако вы можете написать свои исправления в новом файле, который импортирует исходный источник, вносит изменения (и вызывает новую функцию одинаково), а затем меняет ваш PYTHONPATH так, чтобы все ссылки на существующую функцию вызывают вашу измененную версию (вызывающие функции не должны быть мудрее) – eqzx

+0

Ум ... может быть. Но сложная реорганизация импорта и «PYTHONPATH» вряд ли будут чистыми, простыми и/или надежными для сложных базовых кодов. Я мог бы также использовать байт-код и/или переписывание AST для этого. Тем не менее, такая же сложность и надежность. –

+0

Возможно, вам удастся сделать две модификации: 1) новый файл, как описано в моем предыдущем комментарии 2) в '__init __. Py' внутри модуля, где находится исходный' f', настроить импорт на файл, созданный на шаге 1, и выпустить исходную ссылку, чтобы все вызовы 'module_foo.f()' и 'from module_foo import f' заканчивались измененным f. Сложность этого решения не должна масштабироваться с количеством вызовов функций или сложностью кодовой базы. – eqzx