Python: эмулировать pass-by-reference в C-стиле для переменных

У меня есть структура с некоторым C-подобным языком. Теперь я переписываю эту структуру, и язык заменяется на Python.

Мне нужно найти подходящую замену Python для следующей конструкции кода:

SomeFunction(&arg1) 

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

Мои идеи:

  1. просто верните значение, как v = SomeFunction(arg1)

    не так хорошо, потому что моя общая функция может иметь множество аргументов, таких как SomeFunction(1,2,'qqq','vvv',.... and many more) и я хочу дать пользователю возможность получить значение она хочет.

  2. Возвратите коллекцию всех аргументов независимо от того, изменили они или нет, например: resulting_list = SomeFunction(1,2,'qqq','vvv',.... and many more) interesting_value = resulting_list[3] resulting_list = SomeFunction(1,2,'qqq','vvv',.... and many more) interesting_value = resulting_list[3]

    это можно улучшить, указав имена на значения и возвращающий словарь interesting_value = resulting_list['magic_value1']

    Это не хорошо, потому что у нас есть конструкции вроде

     DoALotOfStaff( [SomeFunction1(1,2,3,&arg1,'qq',val2), SomeFunction2(1,&arg2,v1), AnotherFunction(), ... ], flags1, my_var,... ) 

    И я не хотел бы загружать пользователя со списком переменных, с именами или индексами, которые она (пользователь) должна знать. Здесь очень полезны виды ссылок …

Окончательный ответ

Я собрал все ответы своими идеями и смог подготовить решение. Оно работает.

  1. Применение

     SomeFunction(1,12, get.interesting_value) AnotherFunction(1, get.the_val, 'qq') 
  2. объяснение

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

    Ограничение – в настоящее время я поддерживаю только числа и строки, но они достаточны для моего использования.

  3. Реализация

    • написал класс Getter, который переопределяет getattribute и производит любую переменную по требованию

    • все вновь созданные переменные имеют указатель на свой контейнер Getter и набор методов поддержки (self, value)

    • когда set () вызывается, он проверяет, является ли значение int или строкой, и создает объект, наследующий от int или str соответственно, но с добавлением одного и того же метода set (). С помощью этого нового объекта мы заменяем наш экземпляр в контейнере Getter

Спасибо всем. Я буду отмечать как «ответ» ответ, который привел меня на моем пути, но все вы мне кое-как помогли.

Я бы сказал, что ваша лучшая, самая чистая ставка заключалась бы в создании объекта, содержащего значения, которые должны быть переданы и / или изменены – этот единственный объект может быть передан (и будет автоматически передаваться по ссылке), в виде одного параметра и члены могут быть изменены для возврата новых значений.

Это значительно упростит код, и вы сможете с легкостью справиться с необязательными параметрами, по умолчанию и т. Д.

 >>> class C: ... def __init__(self): ... self.a = 1 ... self.b = 2 ... >>> c=C >>> def f(o): ... oa = 23 ... >>> f(c) >>> c <class __main__.C at 0x7f6952c013f8> >>> ca 23 >>> 

Заметка

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

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

Вот как работает Python:

 def func(arg): arg += ['bar'] arg = ['foo'] func(arg) print arg 

Здесь изменение arg автоматически распространяется обратно на вызывающего.

Чтобы это сработало, вы должны быть осторожны, чтобы изменить аргументы вместо того, чтобы повторно привязывать их к новым объектам. Рассмотрим следующее:

 def func(arg): arg = arg + ['bar'] arg = ['foo'] func(arg) print arg 

Здесь func rebinds arg ссылаются на новый список, и arg вызывающего абонента остается неизменным.

Python не приходит с такой встроенной конструкцией. Вы можете создать свой собственный класс, который обеспечивает это поведение, но он будет поддерживать только немного более неудобный синтаксис, когда вызывающий объект будет создавать экземпляр этого класса (эквивалентный указателю в C) перед вызовом ваших функций. Это, вероятно, не стоит. Вместо этого я вернул бы « названный кортеж » (посмотрите его) – я не уверен, что любой из других способов действительно лучше, а некоторые из них более сложны.

Здесь существует большая несогласованность. Недостатки, которые вы описываете против предлагаемых решений, связаны с такими тонкими правилами хорошего дизайна, что ваш вопрос становится недействительным. Вся проблема заключается в том, что ваша функция нарушает принцип единой ответственности и другие связанные с ним рекомендации (функция не должна содержать более 2-3 аргументов и т. Д.). Здесь нет умного компромисса:

  • либо вы принимаете одно из предлагаемых решений (т. е. ответ Стива Барнса относительно ваших собственных оберток или ответ Джона Звинка об использовании названных кортежей) и воздерживаться от фокусировки на хороших тонкостях дизайна (так как в любом случае вся ваша конструкция в любом случае плохая)

  • или вы исправите дизайн. Тогда ваша текущая проблема исчезнет, ​​так как у вас не будет объектов или функций Бога (имя функции в вашем примере – DoALotOfStuff действительно говорит само за себя), чтобы иметь дело больше.