Mendapatkan nama variabel sebagai string

173

Utas ini membahas cara mendapatkan nama fungsi sebagai string dengan Python: Bagaimana cara mendapatkan nama fungsi sebagai string?

Bagaimana saya bisa melakukan hal yang sama untuk suatu variabel? Berbeda dengan fungsi, variabel Python tidak memiliki __name__atribut.

Dengan kata lain, jika saya memiliki variabel seperti:

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

Saya mencari fungsi / atribut, misalnya retrieve_name()untuk membuat DataFrame di Panda dari daftar ini , di mana nama kolom diberikan oleh nama-nama kamus aktual:

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

Jawaban:

19

Menggunakan python-varnamepaket, Anda dapat dengan mudah mengambil nama variabel

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

Dalam kasus Anda, Anda dapat melakukan:

from varname import Wrapper

foo = Wrapper(dict())

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

Atau Anda juga dapat mencoba mengambil nama variabel secara langsung:

from varname import nameof

foo = dict()

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

Saya penulis paket ini. Harap beri tahu saya jika Anda memiliki pertanyaan atau Anda dapat mengirimkan masalah di github.

Panwen Wang
sumber
2
Oke, akhirnya paham! Diinstal menggunakan yang berikut ini pip3 install python-varname==0.1.5; diimpor menggunakanfrom varname import nameof
enter_display_name_here
Entah bagaimana fungsinya tidak bekerja dalam satu lingkaran: test = {} print(varname.nameof(test)) for i in [0]: print(varname.nameof(test))Cetakan pertama memberi test, cetakan dalam lingkaran itu memunculkanVarnameRetrievingError: Callee's node cannot be detected.
Tillus
1
@Tillus Diperbaiki padav0.2.0
Panwen Wang
107

Satu-satunya objek dalam Python yang memiliki nama kanonik adalah modul, fungsi, dan kelas, dan tentu saja tidak ada jaminan bahwa nama kanonik ini memiliki arti dalam ruang nama apa pun setelah fungsi atau kelas didefinisikan atau modul diimpor. Nama-nama ini juga dapat dimodifikasi setelah objek dibuat sehingga mereka tidak selalu dapat dipercaya.

Apa yang ingin Anda lakukan tidak mungkin tanpa secara rekursif berjalan di pohon objek bernama ; nama adalah referensi satu arah ke suatu objek. Objek Python umum atau variasi taman tidak berisi referensi untuk namanya. Bayangkan jika setiap bilangan bulat, setiap dict, setiap daftar, setiap Boolean perlu mempertahankan daftar string yang mewakili nama yang merujuknya! Ini akan menjadi mimpi buruk implementasi, dengan sedikit manfaat bagi programmer.

baik hati
sumber
7
Terima kasih. Tapi mengapa Python melakukan ini untuk fungsi? (Yaitu satu jenis objek Python)
Amelio Vazquez-Reina
1
@indall: jangan lupa modul: mereka memiliki nama kanonik juga. Juga, jangan lupa bahwa __name__selalu dapat dimodifikasi, yang berarti bahwa nama "kanonik" mungkin bukan nama yang dapat digunakan untuk mendapatkan objek (misalnya saat membuat kelas secara dinamis).
nneonneo
8
Pernyataan Anda "itu hanya tidak mungkin" adalah False, seperti @ scohe001 menunjukkan . Database nama variabel Python sama seperti DB relasional lainnya, Anda selalu dapat mencari objek terkait di "mundur" dan mengembalikan yang pertama ditemukan atau seluruh rangkaian nama variabel yang valid untuk setiap variabel yang diberikan.
Hobs
3
@ hobs Anda secara teknis benar ... jenis terbaik yang benar. Namun dalam praktiknya, ada begitu banyak nama potensial untuk suatu objek sehingga lebih sulit daripada layak untuk mendapatkannya.
ramah
1
@indall Saya kira Anda benar jika ambang "sepadan" Anda adalah O (1). loop scohe001 adalah O (N).
Hobs
82

Bahkan jika nilai variabel tidak menunjuk kembali ke nama, Anda memiliki akses ke daftar setiap variabel yang ditugaskan dan nilainya, jadi saya kagum bahwa hanya satu orang yang menyarankan perulangan di sana untuk mencari nama var Anda.

Seseorang yang disebutkan pada jawaban itu bahwa Anda mungkin harus berjalan tumpukan dan memeriksa semua orang lokal dan global untuk menemukan foo, tetapi jika fooditugaskan dalam lingkup di mana Anda memanggil retrieve_namefungsi ini , Anda dapat menggunakan inspectitu current frameuntuk membuat Anda semua variabel-variabel lokal .

Penjelasan saya mungkin agak terlalu bertele-tele (mungkin saya seharusnya menggunakan kata "foo" lebih sedikit), tapi di sini caranya akan terlihat dalam kode ( Perhatikan bahwa jika ada lebih dari satu variabel yang ditugaskan ke nilai yang sama, Anda akan dapatkan kedua nama variabel tersebut ):

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)

Jika Anda memanggil fungsi ini dari fungsi lain, sesuatu seperti:

def foo(bar):
    return retrieve_name(bar)

foo(baz)

Dan Anda menginginkan, bazalih - alih bar, Anda hanya perlu kembali lingkup lebih jauh. Ini dapat dilakukan dengan menambahkan ekstra .f_backdalam caller_local_varsinisialisasi.

Lihat contoh di sini: ideone

scohe001
sumber
1
@theodox Saya benar-benar setuju, karena ini mungkin akan bertindak dengan import hooks, ironpython, danjython
scohe001
5
Ini tidak akan berhasil. variabel atom tidak memiliki nama sebagai atribut. Jadi jika sudah a, b = 2, 2, retrieve_name(a)dan retrieve_name(b)keduanya akan kembali ['a', 'b']atau['b', 'a']
tomas
1
@tomas Sebenarnya, itu adalah detail implementasi dari optimasi cPython di mana integer di bawah 255 pada dasarnya adalah lajang, sehingga setiap variabel yang ditugaskan nilai-nilai tersebut akan secara efektif kembali trueuntuk isperbandingan
Toote
1
apakah ada mod ini untuk mendapatkan nama var yang lulus? mis. def foo(bar): retrieve_name(bar)akan selalu mengembalikan bar, tetapi bagaimana jika Anda ingin foo(baz)kembali baz) bukan bar?
SumNeuron
1
@SumNeuron Anda hanya perlu memodifikasi baris yang menetapkan callers_local_varsuntuk pergi satu lingkup lebih jauh ke belakang sehingga akan melihat lingkup apa pun yang memanggil foo. Ubah baris menjadi inspect.currentframe().f_back.f_back.f_locals.items()(perhatikan tambahan f_back).
scohe001
37

Dengan Python 3.8 orang cukup menggunakan fitur debugging f-string:

>>> foo = dict()
>>> f'{foo=}'.split('=')[0]
'foo' 
Aivar Paalberg
sumber
Bagus! Dan hanya jika Anda ingin mendapatkan nama properti, bukan objek yang Anda bisaf'{foo=}'.split('=')[0].split('.')[-1]
Mickael V.
30

Pada python3, fungsi ini akan mendapatkan nama paling luar di stack:

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]

Ini berguna di mana saja pada kode. Melintasi tumpukan yang terbalik mencari pertandingan pertama.

juan Isaza
sumber
Pekerjaan yang baik! Meskipun saya mencobanya retrieve_name(SomeClass.some_attribute)tidak berhasil. Bisakah Anda membantu lebih jauh tentang itu?
Nam G VU
Ini berjuang dengan variabel boolean. Saya berakhir denganstop_on_error
SumNeuron
26

Saya tidak percaya ini mungkin. Perhatikan contoh berikut:

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

Titik adan bke objek yang sama, tetapi objek tidak dapat mengetahui variabel apa yang mengarah ke sana.

korylprince
sumber
2
Tentunya hubungan dapat dibuat dua arah dengan memperluas penghitungan referensi . Jawaban ini (dan beberapa lainnya) bahkan menyediakan implementasi.
Shayaan
17
def name(**variables):
    return [x for x in variables]

Ini digunakan seperti ini:

name(variable=variable)
fdvfcges
sumber
12
Ini tidak akan mengembalikan nama variabel yang diinginkan, tetapi "variabel". Misalnya - using name(variable=variable)akan menampilkan ['variable'], dan menggunakan tidakname(variable=another_variable) akan menghasilkan melainkan . ['another_variable']['variable']
DalyaG
1
Sebenarnya itu berfungsi seperti yang diharapkan. Anda hanya perlu mengganti kedua «variabel» dengan variabel Anda. Ini akan mengembalikan daftar satu elemen dengan string nama variabel pertama . Misalnya: >>> a = [] >>> b = a >>> name(a=b) ['a'] >>> name(b=a) ['b']`
Alguem Meugla
8

Inilah satu pendekatan. Saya tidak akan merekomendasikan ini untuk hal yang penting, karena itu akan sangat rapuh. Tapi itu bisa dilakukan.

Buat fungsi yang menggunakan inspectmodul untuk menemukan kode sumber yang memanggilnya. Kemudian Anda dapat mengurai kode sumber untuk mengidentifikasi nama variabel yang ingin Anda ambil. Misalnya, inilah fungsi yang dipanggil autodictyang mengambil daftar variabel dan mengembalikan kamus nama variabel pemetaan ke nilai-nilai mereka. Misalnya:

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

Akan memberi:

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

Memeriksa kode sumber itu sendiri lebih baik daripada mencari melalui locals()atau globals()karena pendekatan yang terakhir tidak memberi tahu Anda mana dari variabel yang Anda inginkan.

Bagaimanapun, inilah kodenya:

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

Tindakan terjadi di baris dengan inspect.getouterframes, yang mengembalikan string dalam kode yang dipanggil autodict.

Kelemahan yang jelas untuk jenis sihir ini adalah bahwa ia membuat asumsi tentang bagaimana kode sumber terstruktur. Dan tentu saja, itu tidak akan berfungsi sama sekali jika dijalankan di dalam interpreter.

Zachary Ernst
sumber
Halo, saya hanya punya tiga pertanyaan: 1. Mengapa "1"? 2. Mengapa "4"? 3. Mengapa "0"? :)
Piotr Wasilewicz
7

Saya menulis paket sihir untuk melakukan sihir semacam ini dengan kuat. Kamu bisa menulis:

from sorcery import dict_of

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

dan meneruskannya ke konstruktor dataframe. Ini setara dengan:

columns = dict(n_jobs=n_jobs, users=users, queues=queues, priorities=priorities)
Alex Hall
sumber
6
>>> locals()['foo']
{}
>>> globals()['foo']
{}

Jika Anda ingin menulis fungsi Anda sendiri, bisa dilakukan sedemikian rupa sehingga Anda bisa memeriksa variabel yang ditentukan dalam lokal kemudian memeriksa global. Jika tidak ada yang ditemukan, Anda dapat membandingkan pada id () untuk melihat apakah variabel menunjuk ke lokasi yang sama dalam memori.

Jika variabel Anda ada di kelas, Anda bisa menggunakan className. dict .keys () atau vars (self) untuk melihat apakah variabel Anda telah didefinisikan.

blakev
sumber
Bagaimana jika namanya ada dalam bingkai penelepon? Maka Anda harus konyol hal-hal seperti berjalan tumpukan dan memeriksa semua orang localsdan globals... dan Anda berisiko salah nama jika tidak ada yang benar-benar ada. Ini adalah satu ton pekerjaan tanpa hasil yang bermanfaat.
nneonneo
seluruh pertanyaan itu konyol ... tetapi jika itu sesuatu yang ingin dia lakukan, itu mungkin. Sejauh memeriksa keberadaan Anda dapat menggunakan Globals (). Setdefault (var, <objek jenis baru (var)) untuk membuat sesuatu ketika tidak ada apa pun di sana. Saya tidak begitu yakin untuk apa dia menginginkannya, tapi mungkin belajar bahwa variabel disimpan oleh ruang lingkup dia bisa mencari sesuatu yang lebih baik.
blakev
3

Dalam Python, defdan classkata kunci akan mengikat nama tertentu ke objek yang mereka tetapkan (fungsi atau kelas). Demikian pula, modul diberi nama berdasarkan disebut sesuatu yang spesifik dalam sistem file. Dalam ketiga kasus, ada cara yang jelas untuk menetapkan nama "kanonik" ke objek yang dimaksud.

Namun, untuk jenis objek lainnya, nama kanonik semacam itu mungkin tidak ada . Misalnya, perhatikan elemen daftar. Elemen-elemen dalam daftar tidak disebutkan secara individual, dan sangat mungkin bahwa satu-satunya cara untuk merujuk mereka dalam suatu program adalah dengan menggunakan indeks daftar pada daftar yang berisi. Jika daftar objek seperti itu diteruskan ke fungsi Anda, Anda tidak mungkin dapat menetapkan pengidentifikasi yang berarti untuk nilai.

Python tidak menyimpan nama di sisi kiri penugasan ke objek yang ditugaskan karena:

  1. Ini akan membutuhkan mencari tahu nama mana yang "kanonik" di antara banyak objek yang saling bertentangan,
  2. Tidak masuk akal untuk objek yang tidak pernah ditugaskan ke nama variabel eksplisit,
  3. Itu akan sangat tidak efisien,
  4. Secara harfiah tidak ada bahasa lain yang melakukan itu.

Jadi, misalnya, fungsi yang didefinisikan menggunakan lambdaakan selalu memiliki "nama" <lambda>, bukan nama fungsi tertentu.

Pendekatan terbaik adalah dengan meminta pemanggil untuk memberikan daftar nama (opsional). Jika mengetik '...','...'terlalu rumit, Anda bisa menerima mis. String tunggal yang berisi daftar nama yang dipisahkan koma (seperti namedtuplehalnya).

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

lokal () - Mengembalikan kamus yang berisi variabel lokal lingkup saat ini. dengan mengulangi kamus ini kita dapat memeriksa kunci yang memiliki nilai sama dengan variabel yang didefinisikan, hanya mengekstrak kunci akan memberi kita teks variabel dalam format string.

dari (setelah sedikit perubahan) https://www.tutorialspoint.com/How-to-get-a-variable-name-as-a-string-in-Python

Anshu Pandey
sumber
2

Saya pikir sangat sulit untuk melakukan ini dengan Python karena fakta sederhana bahwa Anda tidak akan pernah tahu nama variabel yang Anda gunakan. Jadi, dalam contohnya, Anda dapat melakukan:

Dari pada:

list_of_dicts = [n_jobs, users, queues, priorities]

dict_of_dicts = {"n_jobs" : n_jobs, "users" : users, "queues" : queues, "priorities" : priorities}
Joe Camel
sumber
2

hanyalah cara lain untuk melakukan ini berdasarkan konten dari variabel input:

(mengembalikan nama variabel pertama yang cocok dengan variabel input, jika tidak Tidak ada. Seseorang dapat memodifikasinya untuk mendapatkan semua nama variabel yang memiliki konten yang sama dengan variabel input)

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
ses
sumber
2

Fungsi ini akan mencetak nama variabel dengan nilainya:

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
iDilip
sumber
2

Saya punya metode, dan meskipun bukan yang paling efisien ... itu berhasil! (dan itu tidak melibatkan modul mewah).

Pada dasarnya ini membandingkan ID Variabel Anda dengan global () ID Variabel , lalu mengembalikan nama pertandingan.

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
Amr Sharaki
sumber
1

Jika tujuannya adalah untuk membantu Anda melacak variabel Anda, Anda dapat menulis fungsi sederhana yang memberi label pada variabel dan mengembalikan nilai dan jenisnya. Sebagai contoh, misalkan i_f = 3.01 dan Anda membulatkannya ke integer yang disebut i_n untuk digunakan dalam kode, dan kemudian membutuhkan string i_s yang akan masuk ke laporan.

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)

Ini mencetak ke jendela di setiap panggilan untuk keperluan debugging dan juga menghasilkan string untuk laporan tertulis. Satu-satunya downside adalah bahwa Anda harus mengetik variabel dua kali setiap kali Anda memanggil fungsi.

Saya seorang pemula Python dan menemukan cara ini sangat berguna untuk mencatat upaya saya ketika saya memprogram dan mencoba untuk mengatasi semua objek dalam Python. Salah satu cacatnya adalah whatis () gagal jika ia memanggil fungsi yang dijelaskan di luar prosedur di mana ia digunakan. Misalnya, int (i_f) adalah panggilan fungsi yang valid hanya karena fungsi int diketahui oleh Python. Anda dapat memanggil whatis () menggunakan int (i_f ** 2), tetapi jika karena alasan yang aneh Anda memilih untuk mendefinisikan fungsi yang disebut int_squared, ia harus dideklarasikan di dalam prosedur di mana whatis () digunakan.

Guy Vandegrift
sumber
1

Mungkin ini bisa bermanfaat:

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

Fungsi melewati daftar ID nilai dari lingkup global (namespace dapat diedit), menemukan indeks var / fungsi yang diinginkan / diperlukan berdasarkan ID-nya, dan kemudian mengembalikan nama dari daftar nama global berdasarkan pada indeks yang diakuisisi.

Reitsch-Z2
sumber
1

Metode berikut tidak akan mengembalikan nama variabel tetapi menggunakan metode ini Anda dapat membuat bingkai data dengan mudah jika variabel tersedia dalam cakupan 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
pengguna5828964
sumber
0

Untuk konstanta, Anda dapat menggunakan enum, yang mendukung pengambilan namanya.

Dana
sumber
0

Saya mencoba untuk mendapatkan nama dari memeriksa penduduk setempat, tetapi tidak dapat memproses var seperti [1], b.val. Setelah itu, saya mendapat ide baru --- dapatkan nama var dari kode, dan saya coba succ! kode seperti di bawah ini:

#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 ""
Ryan
sumber
0

Anda dapat mencoba yang berikut ini untuk mengambil nama fungsi yang Anda tetapkan (meskipun tidak berfungsi untuk fungsi bawaan):

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
Sandipan Dey
sumber