Obtendo o nome de uma variável como uma string


173

Este tópico discute como obter o nome de uma função como uma string no Python: Como obter o nome de uma função como uma string?

Como posso fazer o mesmo para uma variável? Ao contrário de funções, as variáveis ​​Python não têm o __name__atributo

Em outras palavras, se eu tiver uma variável como:

foo = dict()
foo['bar'] = 2

Eu estou procurando uma função / atributo, por exemplo retrieve_name(), para criar um DataFrame no Pandas a partir desta lista , onde os nomes das colunas são dados pelos nomes dos dicionários reais:

# List of dictionaries for my DataFrame
list_of_dicts = [n_jobs, users, queues, priorities]
columns = [retrieve_name(d) for d in list_of_dicts] 

Respostas:


19

Usando o python-varnamepacote, você pode recuperar facilmente o nome das variáveis

https://github.com/pwwang/python-varname

No seu caso, você pode fazer:

from varname import Wrapper

foo = Wrapper(dict())

# foo.name == 'foo'
# foo.value == {}
foo.value['bar'] = 2

Ou você também pode tentar recuperar o nome da variável diretamente:

from varname import nameof

foo = dict()

fooname = nameof(foo)
# fooname == 'foo'

Eu sou o autor deste pacote. Entre em contato se tiver alguma dúvida ou se pode enviar problemas no github.


2
OK, finalmente entendi! Instalado usando o seguinte pip3 install python-varname==0.1.5; importado usandofrom varname import nameof
enter_display_name_here

De alguma forma, a função não funciona em um loop: test = {} print(varname.nameof(test)) for i in [0]: print(varname.nameof(test))a primeira impressão fornece test, a impressão no loop aumentaVarnameRetrievingError: Callee's node cannot be detected.
Tillus

1
@Tillus Fixed atv0.2.0
Panwen Wang

107

Os únicos objetos no Python que têm nomes canônicos são módulos, funções e classes e, é claro, não há garantia de que esse nome canônico tenha algum significado em qualquer espaço de nome após a função ou classe ter sido definida ou o módulo importado. Esses nomes também podem ser modificados após a criação dos objetos, para que eles nem sempre sejam particularmente confiáveis.

O que você deseja fazer não é possível sem percorrer recursivamente a árvore de objetos nomeados ; um nome é uma referência unidirecional para um objeto. Um objeto Python comum ou de variedade de jardim não contém referências a seus nomes. Imagine se todos os números inteiros, todos os ditados, todas as listas e todos os booleanos precisassem para manter uma lista de strings que representassem nomes que se referiam a ele! Seria um pesadelo de implementação, com pouco benefício para o programador.


7
Obrigado. Mas por que o Python faz isso para funções então? (ou seja, tipo de um pitão objetos)
Amelio Vazquez-Reina

1
@kindall: não se esqueça dos módulos: eles também têm nomes canônicos. Além disso, não esqueça que __name__sempre pode ser modificado, o que significa que o nome "canônico" pode não ser um nome que possa ser usado para obter o objeto (por exemplo, ao criar classes dinamicamente).
Neyonneo

8
Sua declaração "simplesmente não é possível" é False, como @ scohe001 mostrou . O banco de dados de nomes de variáveis ​​do Python é como qualquer outro banco de dados relacional. Você sempre pode procurar objetos relacionados em "reverse" e retornar o primeiro encontrado ou todo o conjunto de nomes de variáveis ​​válidos para qualquer variável.
Janelas

3
@ hobs Você está tecnicamente correto ... o melhor tipo de correção. Na prática, no entanto, existem tantos nomes em potencial para um objeto que é mais complicado do que vale a pena tentar obtê-los.
Kindall

1
@kindall Eu acho que você está certo se o seu limite "vale a pena" é O (1). O loop do scohe001 seria O (N).
Placas

82

Mesmo se os valores das variáveis ​​não apontarem para o nome, você terá acesso à lista de todas as variáveis ​​atribuídas e seu valor, por isso estou surpreso que apenas uma pessoa tenha sugerido um loop por lá para procurar seu nome de var.

Alguém mencionou nessa resposta que talvez você precise percorrer a pilha e verificar os locais e globais de todos foo, mas se fooestiver atribuído no escopo em que você está chamando essa retrieve_namefunção, você pode usar inspecto comando 'scurrent frame para obter todas essas variáveis ​​locais .

Minha explicação pode ser um pouco exagerada (talvez eu devesse ter usado um "foo" menos palavras), mas aqui está como ficaria no código ( observe que, se houver mais de uma variável atribuída ao mesmo valor, você obtenha os dois nomes de variáveis ):

import inspect

x,y,z = 1,2,3

def retrieve_name(var):
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()
    return [var_name for var_name, var_val in callers_local_vars if var_val is var]

print retrieve_name(y)

Se você está chamando essa função de outra função, algo como:

def foo(bar):
    return retrieve_name(bar)

foo(baz)

E você deseja que, em bazvez de bar, apenas precise voltar um escopo ainda mais. Isso pode ser feito adicionando um extra .f_backna caller_local_varsinicialização.

Veja um exemplo aqui: ideone


1
@theodox eu concordo absolutamente, pois isto irá provavelmente agir-se com import hooks, ironpythonejython
scohe001

5
Isso não vai funcionar. variáveis ​​atômicas não têm seu nome como um atributo. Então, se você tem a, b = 2, 2, retrieve_name(a)e retrieve_name(b)irão devolver ['a', 'b']ou['b', 'a']
tomas

1
@tomas Na verdade, isso é um detalhe de implementação de uma otimização de CPython em que inteiros abaixo 255 são basicamente singletons, então todas as variáveis atribuídas esses valores efetivamente retornar truepara uma iscomparação
Toote

1
existe um mod disso para obter o nome de um var passado? por exemplo def foo(bar): retrieve_name(bar), sempre retornará a barra, mas e se você quiser foo(baz)retornar baz) em vez de bar?
SumNeuron 01/04/19

1
@ SumNeuron, você apenas precisaria modificar a linha que atribui callers_local_varspara ir um escopo mais para trás, para analisar o escopo de qualquer coisa que esteja chamando foo. Mude a linha para ser inspect.currentframe().f_back.f_back.f_locals.items()(observe o extra f_back).
precisa saber é o seguinte

37

Com o Python 3.8, pode-se simplesmente usar o recurso de depuração de string f:

>>> foo = dict()
>>> f'{foo=}'.split('=')[0]
'foo' 

Agradável ! E apenas se você quiser obter o nome de uma propriedade em vez de um objeto, você podef'{foo=}'.split('=')[0].split('.')[-1]
Mickael V.

30

No python3, essa função obterá o nome mais externo da pilha:

import inspect


def retrieve_name(var):
        """
        Gets the name of var. Does it from the out most frame inner-wards.
        :param var: variable to get name from.
        :return: string
        """
        for fi in reversed(inspect.stack()):
            names = [var_name for var_name, var_val in fi.frame.f_locals.items() if var_val is var]
            if len(names) > 0:
                return names[0]

É útil em qualquer lugar do código. Atravessa a pilha invertida procurando a primeira correspondência.


Bom trabalho! Embora eu tente retrieve_name(SomeClass.some_attribute), não funciona. Você pode ajudar ainda mais nisso?
Nam G VU

Isso luta com variáveis ​​booleanas. Eu acabo comstop_on_error
SumNeuron 02/04/19

26

Não acredito que isso seja possível. Considere o seguinte exemplo:

>>> a = []
>>> b = a
>>> id(a)
140031712435664
>>> id(b)
140031712435664

O ae bapontam para o mesmo objeto, mas o objeto não pode saber quais variáveis ​​apontam para ele.


2
Certamente, o relacionamento pode ser feito de duas maneiras, estendendo a contagem de referências . Essa resposta (e algumas outras) ainda fornece uma implementação.
shayaan

17
def name(**variables):
    return [x for x in variables]

É usado assim:

name(variable=variable)

12
Isso não retornará o nome da variável desejada, mas "variáveis". Por exemplo - usando name(variable=variable)saída de vontade ['variable'], e usando name(variable=another_variable)irá não saída ['another_variable'], mas sim ['variable'].
DalyaG

1
Na verdade, ele funciona como esperado. Você apenas precisa substituir as duas "variáveis" por sua variável. Ele retornará uma lista de um elemento com uma sequência do nome da primeira variável. Por exemplo: >>> a = [] >>> b = a >>> name(a=b) ['a'] >>> name(b=a) ['b']`
Alguem Meugla 28/11/19

8

Aqui está uma abordagem. Eu não recomendaria isso por nada importante, porque será bastante quebradiço. Mas isto pode ser feito.

Crie uma função que use o inspectmódulo para encontrar o código-fonte que o chamou. Em seguida, você pode analisar o código-fonte para identificar os nomes das variáveis ​​que deseja recuperar. Por exemplo, aqui está uma função chamada autodictque pega uma lista de variáveis ​​e retorna um dicionário mapeando nomes de variáveis ​​para seus valores. Por exemplo:

x = 'foo'
y = 'bar'
d = autodict(x, y)
print d

Daria:

{'x': 'foo', 'y': 'bar'}

Inspecionar o código fonte em si é melhor do que pesquisar na locals()ou globals()porque a última abordagem não informa quais variáveis ​​são as desejadas.

De qualquer forma, aqui está o código:

def autodict(*args):
    get_rid_of = ['autodict(', ',', ')', '\n']
    calling_code = inspect.getouterframes(inspect.currentframe())[1][4][0]
    calling_code = calling_code[calling_code.index('autodict'):]
    for garbage in get_rid_of:
        calling_code = calling_code.replace(garbage, '')
    var_names, var_values = calling_code.split(), args
    dyn_dict = {var_name: var_value for var_name, var_value in
                zip(var_names, var_values)}
    return dyn_dict

A ação acontece na linha com inspect.getouterframes, que retorna a string no código que chamou autodict.

A desvantagem óbvia para esse tipo de mágica é que ela faz suposições sobre como o código-fonte é estruturado. E, é claro, não funcionará se for executado dentro do intérprete.


Olá, tenho apenas três perguntas: 1. Por que "1"? 2. Por que "4"? 3. Por que "0"? :)
Piotr Wasilewicz 29/08/19

7

Eu escrevi a feitiçaria de pacotes para fazer esse tipo de mágica de maneira robusta. Você pode escrever:

from sorcery import dict_of

columns = dict_of(n_jobs, users, queues, priorities)

e passe isso para o construtor de quadro de dados. É equivalente a:

columns = dict(n_jobs=n_jobs, users=users, queues=queues, priorities=priorities)

6
>>> locals()['foo']
{}
>>> globals()['foo']
{}

Se você quisesse escrever sua própria função, isso poderia ser feito para que você pudesse procurar uma variável definida em locais e depois verificar globais. Se nada for encontrado, você poderá comparar no id () para ver se a variável aponta para o mesmo local na memória.

Se sua variável estiver em uma classe, você poderá usar className. dict .keys () ou vars (self) para ver se sua variável foi definida.


E se o nome estiver em um quadro de chamada? Então você teria que fazer coisas tolas, como andar na pilha e checar as pessoas localse globals... e corre o risco de errar o nome, se não existir. É uma tonelada de trabalho para nenhum ganho útil real.
Nneonneo

a pergunta toda é boba ... mas se é algo que ele queria fazer, é possível. No que se refere à verificação da existência, você pode usar Globals (). Setdefault (var, <novo objeto do tipo (var)) para criar algo quando nada está lá. Não sei exatamente o que ele quer, mas talvez aprendendo que as variáveis ​​são mantidas pelo escopo, ele poderia descobrir algo melhor.
blakev

3

Em Python, o defeclass palavras-chave vincularão um nome específico ao objeto que definem (função ou classe). Da mesma forma, os módulos recebem um nome em virtude de serem chamados de algo específico no sistema de arquivos. Nos três casos, há uma maneira óbvia de atribuir um nome "canônico" ao objeto em questão.

No entanto, para outros tipos de objetos, esse nome canônico pode simplesmente não existir . Por exemplo, considere os elementos de uma lista. Os elementos da lista não são nomeados individualmente e é inteiramente possível que a única maneira de se referir a eles em um programa seja usando índices de lista na lista que o contém. Se essa lista de objetos tiver sido passada para sua função, você não poderá atribuir identificadores significativos aos valores.

Python não salva o nome no lado esquerdo de uma atribuição no objeto atribuído porque:

  1. Seria necessário descobrir qual nome era "canônico" entre vários objetos conflitantes,
  2. Não faria sentido para objetos que nunca são atribuídos a um nome explícito de variável,
  3. Seria extremamente ineficiente,
  4. Literalmente, nenhuma outra língua existente faz isso.

Assim, por exemplo, as funções definidas usando lambdasempre terão o "nome" <lambda>, em vez de um nome de função específico.

A melhor abordagem seria simplesmente pedir ao chamador que passasse uma lista (opcional) de nomes. Se digitar o '...','...'é muito complicado, você pode aceitar, por exemplo, uma única string contendo uma lista de nomes separados por vírgula (como namedtuplefaz).


3
>> my_var = 5
>> my_var_name = [ k for k,v in locals().items() if v == my_var][0]
>> my_var_name 
'my_var'

locals () - Retorna um dicionário que contém as variáveis ​​locais do escopo atual. iterando através deste dicionário, podemos verificar a chave que tem um valor igual à variável definida, apenas extrair a chave fornecerá o texto da variável no formato de string.

from (depois de algumas alterações) https://www.tutorialspoint.com/How-to-get-a-variable-name-as-a-string-in-Python


2

Eu acho que é tão difícil fazer isso no Python, pelo simples fato de você nunca saber o nome da variável que está usando. Então, no exemplo dele, você poderia fazer:

Ao invés de:

list_of_dicts = [n_jobs, users, queues, priorities]

dict_of_dicts = {"n_jobs" : n_jobs, "users" : users, "queues" : queues, "priorities" : priorities}

2

apenas outra maneira de fazer isso com base no conteúdo da variável de entrada:

(retorna o nome da primeira variável que corresponde à variável de entrada, caso contrário, Nenhum. É possível modificá-la para obter todos os nomes de variáveis ​​com o mesmo conteúdo que a variável de entrada)

def retrieve_name(x, Vars=vars()):
    for k in Vars:
        if type(x) == type(Vars[k]):
            if x is Vars[k]:
                return k
    return None

2

Esta função imprimirá o nome da variável com seu valor:

import inspect

def print_this(var):
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()
    print(str([k for k, v in callers_local_vars if v is var][0])+': '+str(var))
***Input & Function call:***
my_var = 10

print_this(my_var)

***Output**:*
my_var: 10

2

Eu tenho um método e , embora não seja o mais eficiente ... funciona! (e não envolve nenhum módulo sofisticado).

Basicamente, ele compara o seu ID do Variable para globals () IDs Variáveis , em seguida, retorna o nome do jogo.

def getVariableName(variable, globalVariables=globals().copy()):
    """ Get Variable Name as String by comparing its ID to globals() Variables' IDs

        args:
            variable(var): Variable to find name for (Obviously this variable has to exist)

        kwargs:
            globalVariables(dict): Copy of the globals() dict (Adding to Kwargs allows this function to work properly when imported from another .py)
    """
    for globalVariable in globalVariables:
        if id(variable) == id(globalVariables[globalVariable]): # If our Variable's ID matches this Global Variable's ID...
            return globalVariable # Return its name from the Globals() dict

1

Se o objetivo é ajudá-lo a acompanhar suas variáveis, você pode escrever uma função simples que rotule a variável e retorne seu valor e tipo. Por exemplo, suponha que i_f = 3.01 e o arredonde para um número inteiro chamado i_n para usar em um código e, em seguida, precise de uma string i_s que entrará em um relatório.

def whatis(string, x):
    print(string+' value=',repr(x),type(x))
    return string+' value='+repr(x)+repr(type(x))
i_f=3.01
i_n=int(i_f)
i_s=str(i_n)
i_l=[i_f, i_n, i_s]
i_u=(i_f, i_n, i_s)

## make report that identifies all types
report='\n'+20*'#'+'\nThis is the report:\n'
report+= whatis('i_f ',i_f)+'\n'
report+=whatis('i_n ',i_n)+'\n'
report+=whatis('i_s ',i_s)+'\n'
report+=whatis('i_l ',i_l)+'\n'
report+=whatis('i_u ',i_u)+'\n'
print(report)

Isso é impresso na janela em cada chamada para fins de depuração e também gera uma sequência para o relatório escrito. A única desvantagem é que você deve digitar a variável duas vezes sempre que chamar a função.

Eu sou um novato em Python e achei essa maneira muito útil de registrar meus esforços enquanto programa e tento lidar com todos os objetos em Python. Uma falha é que whatis () falha se chama uma função descrita fora do procedimento em que é usada. Por exemplo, int (i_f) era uma chamada de função válida apenas porque a função int é conhecida pelo Python. Você pode chamar whatis () usando int (i_f ** 2), mas se por algum motivo estranho você escolher definir uma função chamada int_squared, ela deverá ser declarada dentro do procedimento em que whatis () é usado.


1

Talvez isso possa ser útil:

def Retriever(bar):
    return (list(globals().keys()))[list(map(lambda x: id(x), list(globals().values()))).index(id(bar))]

A função percorre a lista de IDs de valores do escopo global (o namespace pode ser editado), localiza o índice da var ou função desejada / necessária com base em seu ID e, em seguida, retorna o nome da lista de nomes globais com base em no índice adquirido.


1

O método a seguir não retornará o nome da variável, mas, usando esse método, você poderá criar um quadro de dados facilmente se a variável estiver disponível no escopo global.

class CustomDict(dict):
    def __add__(self, other):
        return CustomDict({**self, **other})

class GlobalBase(type):
    def __getattr__(cls, key):
        return CustomDict({key: globals()[key]})

    def __getitem__(cls, keys):
        return CustomDict({key: globals()[key] for key in keys})

class G(metaclass=GlobalBase):
    pass

x, y, z = 0, 1, 2

print('method 1:', G['x', 'y', 'z']) # Outcome: method 1: {'x': 0, 'y': 1, 'z': 2}
print('method 2:', G.x + G.y + G.z) # Outcome: method 2: {'x': 0, 'y': 1, 'z': 2}

A = [0, 1]
B = [1, 2]
pd.DataFrame(G.A + G.B) # It will return a data frame with A and B columns

0

Para constantes, você pode usar uma enumeração, que suporta a recuperação de seu nome.


0

Eu tento obter o nome de inspecionar os locais, mas ele não pode processar var likes a [1], b.val. Depois disso, tive uma nova idéia - obtenha o nome var do código e tento succ! código como abaixo:

#direct get from called function code
def retrieve_name_ex(var):
    stacks = inspect.stack()
    try:
        func = stacks[0].function
        code = stacks[1].code_context[0]
        s = code.index(func)
        s = code.index("(", s + len(func)) + 1
        e = code.index(")", s)
        return code[s:e].strip()
    except:
        return ""

0

Você pode tentar o seguinte para recuperar o nome de uma função que você definiu (embora não funcione para funções internas):

import re
def retrieve_name(func):
    return re.match("<function\s+(\w+)\s+at.*", str(func)).group(1)

def foo(x):
    return x**2

print(retrieve_name(foo))
# foo
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.