Bir değişkenin adını dize olarak alma


173

Bu iş parçacığı, bir işlevin adının bir dize olarak Python'da nasıl alınacağını açıklar: Bir işlev adı nasıl dize olarak alınır?

Aynı şeyi bir değişken için nasıl yapabilirim? İşlevlerin aksine, Python değişkenleri bu özelliğe sahip değildir __name__.

Başka bir deyişle, böyle bir değişkenim varsa:

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

Ben bir işlev / öznitelik, örneğin arıyorum retrieve_name()amacıyla bu listeden Pandalar bir DataFrame oluşturmak , sütun adları gerçek sözlüklerin adlarıyla verilmiştir:

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

Yanıtlar:


19

python-varnamePaketi kullanarak değişkenlerin adını kolayca alabilirsiniz

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

Sizin durumunuzda şunları yapabilirsiniz:

from varname import Wrapper

foo = Wrapper(dict())

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

Veya değişken adını doğrudan almayı da deneyebilirsiniz:

from varname import nameof

foo = dict()

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

Ben bu paketin yazarıyım. Herhangi bir sorunuz varsa veya github ile ilgili sorularınızı iletmek için lütfen bize bildirin.


2
Tamam, sonunda anladım! Aşağıdakiler kullanılarak kurulur pip3 install python-varname==0.1.5; kullanılarak içe aktarıldıfrom varname import nameof
enter_display_name_here

Bir şekilde işlev bir döngüde çalışmaz: test = {} print(varname.nameof(test)) for i in [0]: print(varname.nameof(test))İlk baskı verir test, döngüdeki baskı yükselirVarnameRetrievingError: Callee's node cannot be detected.
Tillus

1
@Tillus Sabitv0.2.0
Panwen Wang

108

Python'da kanonik isimleri olan tek nesneler modüller, işlevler ve sınıflardır ve elbette bu kanonik adın, işlev veya sınıf tanımlandıktan veya içe aktarılan modülün herhangi bir ad alanında herhangi bir anlamı olduğuna dair bir garanti yoktur. Bu adlar, nesneler oluşturulduktan sonra da değiştirilebilir, böylece her zaman özellikle güvenilir olmayabilirler.

Yapmak istediğiniz şey, adlandırılmış nesnelerin ağacını tekrar tekrar yürümeden mümkün değildir ; ad, bir nesneye tek yönlü başvurudır. Ortak veya bahçe çeşitliliği olan bir Python nesnesi adlarına referans içermez. Her tamsayının, her diktenin, her listenin, her Booleanın kendisine atıfta bulunan isimleri temsil eden dizelerin bir listesini tutması gerektiğini düşünün! Programcıya çok az fayda sağlayan bir uygulama kabusu olurdu.


7
Teşekkürler. Peki Python bunu neden fonksiyonlar için yapıyor? (yani bir tür Python nesnesi)
Amelio Vazquez-Reina

1
@kindall: modülleri unutma: kanonik isimleri de var. Ayrıca, __name__her zaman değiştirilebileceğini unutmayın , yani "kurallı" ad, nesneyi almak için kullanılabilecek bir ad olmayabilir (örneğin, dinamik olarak sınıflar oluştururken).
nneonneo

8
@ Scohe001'in gösterdiği gibi, "bu sadece mümkün değil" ifadeniz Yanlış . Python'un değişken adı veritabanı diğer tüm ilişkisel DB'ler gibidir, her zaman ilgili nesneleri "tersine" arayabilir ve herhangi bir değişken için ilk bulunan veya tüm geçerli değişken isimlerini döndürebilirsiniz.
Ocaklar

3
@hobs Teknik olarak haklısın ... en doğru tür. Bununla birlikte, pratikte, bir nesne için o kadar çok potansiyel isim vardır ki, onları almaya çalışmaktan daha zahmetlidir.
tür

1
@kindall "Buna değer" eşiğiniz O (1) ise haklısınız. scohe001'in döngüsü O (N) olur.
ocaklar

82

Değişken değerleri isme işaret etmese bile, atanan her değişkenin ve değerinin listesine erişebilirsiniz, bu yüzden sadece bir kişinin var adınızı aramak için orada dolaşmayı önerdiğine şaşırdım.

Birisi bu cevapta yığını yürümeniz ve herkesin yerlilerini ve küreselleri bulmanız gerekebileceğinden bahsetmişti foo, ancak foobu retrieve_nameişlevi çağırdığınız kapsamda atanmışsa , bu yerel değişkenlerin tümünü elde etmek için inspect' current frame.

Açıklamam biraz fazla garip olabilir (belki bir "foo" daha az kelime kullanmalıydım), ancak kodda nasıl görüneceğini ( Aynı değere atanmış birden fazla değişken varsa, bu değişken adlarının her ikisini de edinin ):

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)

Bu işlevi başka bir işlevden çağırıyorsanız, şuna benzer:

def foo(bar):
    return retrieve_name(bar)

foo(baz)

Ve bazbunun yerine, barsadece bir kapsamı daha geriye götürmeniz gerekir. Bu .f_back, caller_local_varsbaşlatma işlemine bir fazladan eklenerek yapılabilir .

Burada bir örneğe bakın: ideone


1
@theodox Kesinlikle bu muhtemelen ile hareket edecek şekilde, hemfikir import hooks, ironpythonvejython
scohe001

5
Bu işe yaramaz. atom değişkenlerinin adı öznitelik olarak yoktur. Sahip Yani eğer a, b = 2, 2, retrieve_name(a)ve retrieve_name(b)her ikisi de getiri olacak ['a', 'b']ya['b', 'a']
tomas

1
@tomas Aslında, 255'in altında tamsayıların temelde tekton olduğu bir cPython optimizasyonunun bir uygulama detayıdır, bu nedenle bu değerlere atanan değişkenler truebir iskarşılaştırma için etkili bir şekilde dönecektir
Toote

1
bir var var adını almak için bunun bir mod var mı? örneğin def foo(bar): retrieve_name(bar)İstersen her zaman bar, ama ne dönecektir foo(baz)dönüşüne bazyerine) bar?
SumNeuron

1
@SumNeuron, callers_local_varsbir kapsamı daha ileriye taşımak için atayan hattı değiştirmeniz yeterlidir, böylece ne çağırırsa onu inceler foo. Satırı olacak şekilde değiştirin inspect.currentframe().f_back.f_back.f_locals.items()(ekstralara dikkat edin f_back).
scohe001

37

Python 3.8 ile f-string hata ayıklama özelliğini kullanabilirsiniz:

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

Güzel ! Ve bir nesne yerine bir mülkün adını almak istiyorsanızf'{foo=}'.split('=')[0].split('.')[-1]
Mickael V.Mar

30

Python3'te, bu işlev yığındaki en dış adı alır:

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]

Kodun herhangi bir yerinde kullanışlıdır. İlk eşleşmeyi arayan ters yığını gezdirir.


İyi iş! Denememe rağmen retrieve_name(SomeClass.some_attribute)işe yaramıyor. Bu konuda daha fazla yardımcı olabilir misiniz?
Nam G VU

Bu, boolean değişkenlerle mücadele eder. Ben sonundastop_on_error
SumNeuron

26

Bunun mümkün olduğuna inanmıyorum. Aşağıdaki örneği düşünün:

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

aVe baynı nesneye gelin ama nesne öyle gösteriyor eden değişkenleri bilemez.


2
Şüphesiz ilişki, referans sayımını genişleterek iki yönlü yapılabilir . Bu cevap (ve bazıları) bile bir uygulama sağlar.
Shayaan

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

Bu şekilde kullanılır:

name(variable=variable)

12
Bu, istenen değişkenin adını değil, "değişkenleri" döndürür. Örneğin - kullanarak name(variable=variable)irade çıkışı ['variable']ve kullanma name(variable=another_variable)olacak değil çıktıyı ['another_variable']ziyade ['variable'].
DalyaG

1
Aslında beklendiği gibi çalışıyor. Her iki «değişkeni" de değişkeninizle değiştirmeniz yeterlidir. İlk değişkenin adından oluşan bir dize içeren tek öğeli bir liste döndürür . Örneğin: >>> a = [] >>> b = a >>> name(a=b) ['a'] >>> name(b=a) ['b']``
Alguem Meugla

8

İşte bir yaklaşım. Bunu önemli bir şey için tavsiye etmem, çünkü oldukça kırılgan olacak. Ama yapılabilir.

inspectOnu çağıran kaynak kodunu bulmak için modülü kullanan bir işlev oluşturun . Ardından, almak istediğiniz değişken adlarını tanımlamak için kaynak kodunu ayrıştırabilirsiniz. Örneğin, burada autodictdeğişkenlerin listesini alan ve sözlük adlarını değişkenlere değerleriyle eşleyen bir işlev verilmiştir . Örneğin:

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

Verecek:

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

Kaynak kodun kendisini incelemek locals()veya içinde arama yapmaktan daha iyidir, globals()çünkü ikinci yaklaşım size hangi değişkenlerin hangilerinin istediğiniz olduğunu söylemez .

Her halükarda, kod:

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

Eylem inspect.getouterframes, çağrılan kodun içindeki dizeyi döndüren satırında gerçekleşir autodict.

Bu tür bir büyünün bariz dezavantajı, kaynak kodunun nasıl yapılandırıldığı hakkında varsayımlar yapmasıdır. Ve elbette, tercüman içinde çalıştırılırsa hiç işe yaramaz.


Merhaba, sadece üç sorum var: 1. Neden "1"? 2. Neden "4"? 3. Neden "0"? :)
Piotr Wasilewicz

7

Bu büyüyü sağlam bir şekilde yapmak için sihirbazlık paketini yazdım . Yazabilirsin:

from sorcery import dict_of

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

ve bunu veri çerçevesi yapıcısına iletir. Şuna eşittir:

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

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

Kendi işlevinizi yazmak istiyorsanız, yerellerde tanımlanan bir değişkeni kontrol edip globalleri kontrol edebileceğiniz şekilde yapılabilir. Hiçbir şey bulunmazsa, değişkenin bellekteki aynı konumu gösterip göstermediğini görmek için id () yöntemiyle karşılaştırabilirsiniz.

Değişkeniniz bir sınıftaysa, className kullanabilirsiniz. değişkeninizin tanımlanıp tanımlanmadığını görmek için .keys () veya vars (self) dict .


Adı arayan bir çerçevedeyse? O zaman yığını yürümek ve herkesin kontrol etmek gibi aptalca şeyler gerekir localsve globals... ve aslında hiçbiri varsa adı yanlış alma riskiniz var. Gerçek bir kazanç elde etmek için bir ton çalışma.
nneonneo

tüm soru aptalca ... ama yapmak istediği bir şey varsa, bu mümkün. Varlığı kontrol ederken, hiçbir şey olmadığında bir şey oluşturmak için Globals (). Setdefault (var, <yeni tip (var)) nesnesini kullanabilirsiniz. Ne istediğini tam olarak bilmiyorum, ama belki de değişkenlerin kapsam tarafından tutulduğunu öğrenmek daha iyi bir şey bulabilirdi.
blakev

3

Python'da defve classanahtar sözcükleri, tanımladıkları nesneye (işlev veya sınıf) belirli bir adı bağlar. Benzer şekilde, dosya sistemine özel bir şey olarak adlandırılması nedeniyle modüllere bir ad verilir. Her üç durumda da, söz konusu nesneye "kanonik" bir ad atamanın açık bir yolu vardır.

Bununla birlikte, diğer nesne türleri için, böyle bir kanonik isim basitçe mevcut olmayabilir . Örneğin, bir listenin öğelerini düşünün. Listedeki öğeler tek tek adlandırılmaz ve bir programda bunlara başvurmanın tek yolunun, listedeki liste indekslerini kullanmak tamamen mümkündür. Böyle bir nesne listesi işlevinize aktarıldıysa, değerlere anlamlı tanımlayıcılar atayamazsınız.

Python, bir atamanın sol tarafındaki adı atanan nesneye kaydetmez, çünkü:

  1. Birden fazla çelişen nesne arasında hangi adın "kanonik" olduğunu bulmayı gerektirir,
  2. Asla açık bir değişken adına atanmamış nesneler için bir anlam ifade etmez,
  3. Son derece verimsiz olurdu,
  4. Kelimenin tam anlamıyla var olan başka bir dil bunu yapmaz.

Dolayısıyla, örneğin, kullanılarak tanımlanan işlevler , belirli bir işlev adı yerine lambdaher zaman "ad" a sahip olacaktır <lambda>.

En iyi yaklaşım, arayan kişiden (isteğe bağlı) bir isim listesine geçmesini istemektir. Yazarken '...','...'çok hantalsanız, örneğin virgülle ayrılmış bir ad listesi içeren tek bir dize (olduğu gibi namedtuple) kabul edebilirsiniz.


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 () - Geçerli kapsamın yerel değişkenlerini içeren bir sözlük döndürür. bu sözlüğü tekrarlayarak, tanımlanan değişkene eşit bir değeri olan anahtarı kontrol edebiliriz, sadece anahtarı çıkartmak bize değişken biçimini dize biçiminde verecektir.

from (bir miktar değişiklikten sonra) https://www.tutorialspoint.com/How-to-get-a-variable-name-as-a-string-in-Python


2

Bence Python'da bunu yapmak çok zor çünkü kullandığınız değişkenin adını asla bilemeyeceksiniz. Yani, örneğinde şunları yapabilirsiniz:

Onun yerine:

list_of_dicts = [n_jobs, users, queues, priorities]

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

2

giriş değişkeninin içeriğine göre bunu yapmanın başka bir yolu:

(giriş değişkeniyle eşleşen ilk değişkenin adını döndürür, aksi takdirde Yoktur. Giriş değişkeniyle aynı içeriğe sahip tüm değişken adlarını almak için bunu değiştirebilirsiniz)

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

Bu işlev, değişken adını değeriyle basacaktır:

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

Bir yöntemim var ve en verimli olmasa da ... işe yarıyor! (ve herhangi bir fantezi modül içermez).

Temelde sizin karşılaştırır değişken kimliğini için globaller () Değişkenleri kimlikleri daha sonra eşlemenin adını döndürür.

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

Amaç, değişkenlerinizi takip etmenize yardımcı olmaksa, değişkeni etiketleyen ve değerini ve türünü döndüren basit bir işlev yazabilirsiniz. Örneğin, diyelim ki i_f = 3.01 ve bunu bir kodda kullanmak için i_n adlı bir tamsayıya yuvarlayın ve ardından rapora gidecek bir i_s dizesine ihtiyacınız var.

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)

Bu, hata ayıklama amacıyla her çağrıda pencereye yazdırılır ve ayrıca yazılı rapor için bir dize verir. Tek dezavantajı, işlevi her çağırdığınızda değişkeni iki kez yazmanız gerektiğidir.

Ben bir Python acemi ve ben program ve Python tüm nesnelerle başa çıkmak için çabalarımı kaydetmek için bu çok yararlı bir yol buldum. Bir kusur, whatis () yönteminin kullanıldığı yordamın dışında açıklanan bir işlevi çağırması durumunda başarısız olmasıdır. Örneğin, int (i_f) yalnızca int işlevi Python tarafından bilindiği için geçerli bir işlev çağrısıydı . İnt (i_f ** 2) kullanarak whatis () öğesini çağırabilirsiniz, ancak garip bir nedenle int_squared adlı bir işlev tanımlamayı seçerseniz, whatis () yönteminin kullanıldığı yordamın içinde bildirilmelidir.


1

Belki bu yararlı olabilir:

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

İşlev, global kapsamdaki değerlerin kimlikleri listesine girer (ad alanı düzenlenebilir), kimliğine göre istenen / gerekli var veya işlevin dizinini bulur ve ardından adı genel adlar listesinden döndürür edinilen endekste.


1

Aşağıdaki yöntem değişkenin adını döndürmez, ancak değişken global kapsamda mevcutsa bu yöntemi kullanarak kolayca veri çerçevesi oluşturabilirsiniz.

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

Sabitler için, adını almayı destekleyen bir numaralandırma kullanabilirsiniz.


0

İnceleme yerlilerinden isim almaya çalışıyorum, ancak bir [1], b.val gibi var işleyemiyor. Bundan sonra, yeni bir fikrim var --- koddan var adı almak ve bunu succ deneyin! aşağıdaki gibi kod:

#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

Tanımladığınız bir işlevin adını almak için aşağıdakileri deneyebilirsiniz (yine de yerleşik işlevler için çalışmaz):

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.