вернуть имя переменной извне функции, как строку внутри функции python

Итак, я создал функцию, которая применяет действие (в данном случае точечное умножение массива на синусоиду, но это не имеет значения для моего вопроса) к массиву.

Теперь я создал еще одну функцию, с помощью которой я хочу создать string кода Python, чтобы позже применить первую функцию несколько раз. Вход второй функции может быть либо string, либо array, так что я могу использовать вторую функцию и на ее собственном выходе, если это необходимо. Мой метод получения имени переменной в строке работает вне функции.

Ввод:

var = np.array([[1,3],[2,4]]) # or sometimes var = 'a string'

if type(var)==str:
    var_name = var
else:
    var_name = [ k for k,v in locals().items() if v is var][0]

var_name

Вывод:

'var'

Итак, здесь var — это переменная (массив или строка), переданная функции, в данном случае массив. Оператор if прекрасно возвращает мне его имя.

Однако, когда я использую это внутри своей функции, независимо от того, какой ввод я ей ввожу, на самом деле кажется, что он ищет var в locals(). Почему-то он не берет var из ввода функции.

Определение:

def functionTWO(var, listoflistsofargs=None):
    if type(var)==str:
        var_name = var
    else:
        var_name = [ k for k,v in locals().items() if v is var][0]
    if listoflistsofargs==None:
        return var_name
    command = []
    for i in range(len(listoflistsofargs)):
        if i==0:
            command.append('functionONE(')
            command.append(var_name)
            command.append(',%.17f, %.17f)' % tuple(listoflistsofargs[i]))
        else:
            command.insert(0,'functionONE(')
            command.append(',%.17f, %.17f)' % tuple(listoflistsofargs[i]))
    ''.join(command)
    command[0] = var_name + ' + ' + command[0]
    return ''.join(command)

Ввод:

somearray = np.array([[1,2,3],[1,2,3],[1,2,3]])
args = [[1,3],[6,5]]
command = functionTWO(somearray, args)
command

Вывод:

NameError: name 'var' is not defined

Требуемый результат:

'functionONE(functionONE(somearray, 1, 3), 6, 5)'

Почему listoflistsofargs берется из ввода функции, а var нет? Я указываю var в listcomprehension в определении functionTWO. Обычно, когда я использую понимание списка с вводом функций, он работает нормально. Кто-нибудь знает, почему это не так здесь? Заранее спасибо!

EDIT: Так что я думаю, что ответ нельзя. Реализация классов Марсином выглядит намного чище и примерно того же порядка объема кода. Жаль, что я не мог заставить это работать внутри функции. Для других запретов (на самом деле других идей) об использовании имен переменных в виде строк есть этот вопрос, где я получил указанное выше понимание списка для имен переменных.


person Leo    schedule 09.09.2013    source источник
comment
Почему бы вам просто не передать имя переменной в функцию? Это кажется гораздо меньшим набором текста, чем то, что вы пытаетесь сделать сейчас. Это также было бы более явным и менее подверженным ошибкам.   -  person dr. Sybren    schedule 09.09.2013
comment
@sybren Ну, я хотел иметь возможность выполнять одну и ту же функцию произвольное количество раз на съемочной площадке. Было глупо, что я могу легко ввести это в командной консоли, но я не знал простого способа заставить функцию делать это, поэтому я просто заставил функцию ввести это. Думаю, есть «лучшие» способы. Попробую их выучить :)   -  person Leo    schedule 09.09.2013


Ответы (3)


Вы не можете передать переменную в виде строки*, и вы не должны этого делать.

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

Если это неудобно, обычным решением является объект: определите класс, который содержит как общую переменную, так и методы, которые воздействуют на переменную.

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

def foo():
    return (functionONE,somearray,1,3)

command = foo()
command[0](*command[1:])

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

def evaluator(object):
    def __init__(self,func=None,params=None):
        self.func = func
        self.params = params

    def eval(self,alternativeparams=None):
        if alternativeparams is not None:
           params = alternativeparams
        else:
           params = self.params

        if params is not None:
           evaluatedparams = (item() if callable(item) else item for item in params)
        else: evaluatedparams = None

        if func is not None:
           return self.func(*(evaluatedparams or ()))
        else: return evaluatedparams
    def __call__(self, *params):
        return self.eval(params if params else None)

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

* Это связано с тем, что у переменной есть имя (которое является строкой) и контекст, который отображает имена в строки. Итак, вам нужно, по крайней мере, передать кортеж, чтобы действительно передать переменную.

person Marcin    schedule 09.09.2013
comment
не может - более сильный термин, чем я бы использовал, но да +1 - person Joran Beasley; 09.09.2013
comment
@JoranBeasley Достаточно честно, но, как показывает проблема OP, на самом деле это не переменная, а имя без контекста. - person Marcin; 09.09.2013
comment
@Marcin Очень хорошее объяснение, спасибо! Меня все еще беспокоит, что нет более быстрого способа сделать что-то, что я могу очень быстро набрать в консоли. Ваше решение более элегантно, чем мое (в конце концов я решил зарезервировать глобальную переменную var для этой функции). Чтобы иметь возможность использовать ваше решение, мне придется изучить, что такое классы и как они работают. Но я думаю, что это будет не последний раз, когда они мне понадобятся. Так что спасибо и за ваше решение, и за объяснение, почему мой метод не рекомендуется. - person Leo; 09.09.2013
comment
@ Лео Не за что. Если вы используете глобальную переменную, это снова является признаком того, что вы должны использовать класс. Короче говоря, классы — это то, что вы видите выше: объявление class <classname>(<baseclass>):, за которым следует список функций (методов), которые принимают экземпляр класса в качестве своего первого аргумента (self). Доступ к переменным экземпляра объекта осуществляется с помощью self.<variable name>. Вы создаете экземпляры, вызывая класс (с параметрами для __init__, кроме self). Экземпляры имеют переменные, которые отличаются от переменных в других экземплярах (если они не определены внутри тела класса). - person Marcin; 10.09.2013

ответ ведущего работает в python 3

def get_variable_name(*variable):
    return [ k for k,v in globals().items() if v is variable[0]][0]

пример:

>> test = 'sample string'
>> get_variable_name(test)
'test'
>>

Единственная проблема в том, что это немного грязно.

person Anna    schedule 06.04.2016

Причина, по которой вы получаете ошибку NameError: name 'var' is not defined, когда вы включаете все это в функцию, заключается в следующем: locals().items() относится к локально определенным переменным. на самом деле локально определенные переменные в ваших функциях — это только переменные, определенные внутри функции, и те, которые передаются в качестве аргументов функции.

Как следует из названия, locals().items() является локальным и будет состоять из разных переменных в зависимости от того, где он вызывается в вашем скрипте.

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

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

person presenter    schedule 14.01.2016