Lấy tên của một biến là một chuỗi


173

Chuỗi này thảo luận về cách lấy tên của hàm dưới dạng chuỗi trong Python: Làm thế nào để lấy tên hàm dưới dạng chuỗi?

Làm thế nào tôi có thể làm tương tự cho một biến? Trái ngược với các hàm, các biến Python không có __name__thuộc tính.

Nói cách khác, nếu tôi có một biến như:

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

Tôi đang tìm kiếm một hàm / thuộc tính, ví dụ retrieve_name()để tạo DataFrame trong Pandas từ danh sách này , trong đó các tên cột được đặt theo tên của các từ điển thực tế:

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

Câu trả lời:


18

Sử dụng python-varnamegói, bạn có thể dễ dàng lấy tên của các biến

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

Trong trường hợp của bạn, bạn có thể làm:

from varname import Wrapper

foo = Wrapper(dict())

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

Hoặc bạn cũng có thể thử truy xuất tên biến trực tiếp:

from varname import nameof

foo = dict()

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

Tôi là tác giả của gói này. Vui lòng cho tôi biết nếu bạn có bất kỳ câu hỏi nào hoặc bạn có thể gửi các vấn đề trên github.


2
OK, cuối cùng đã có nó! Cài đặt bằng cách sử dụng sau đây pip3 install python-varname==0.1.5; được nhập bằngfrom varname import nameof
enter_display_name_here

Bằng cách nào đó chức năng không hoạt động trong một vòng lặp: test = {} print(varname.nameof(test)) for i in [0]: print(varname.nameof(test))Bản in đầu tiên cho test, bản in trong vòng lặp tăng lênVarnameRetrievingError: Callee's node cannot be detected.
Tillus

1
@Tillus Đã sửa tạiv0.2.0
Panwen Wang

106

Các đối tượng duy nhất trong Python có tên chính tắc là mô-đun, hàm và lớp và tất nhiên không có gì đảm bảo rằng tên chính tắc này có bất kỳ ý nghĩa nào trong bất kỳ không gian tên nào sau khi hàm hoặc lớp được xác định hoặc mô-đun được nhập. Những tên này cũng có thể được sửa đổi sau khi các đối tượng được tạo để chúng không phải lúc nào cũng đáng tin cậy.

Những gì bạn muốn làm là không thể nếu không đi theo cách đệ quy cây của các đối tượng được đặt tên ; một tên là một tham chiếu một chiều đến một đối tượng. Một đối tượng Python phổ biến hoặc đa dạng trong vườn không chứa tham chiếu đến tên của nó. Hãy tưởng tượng nếu mọi số nguyên, mọi dict, mọi danh sách, mọi Boolean cần để duy trì một danh sách các chuỗi đại diện cho các tên được đề cập đến nó! Nó sẽ là một cơn ác mộng thực hiện, với rất ít lợi ích cho lập trình viên.


7
Cảm ơn. Nhưng tại sao Python làm điều này cho các chức năng sau đó? (tức là một loại đối tượng Python)
Amelio Vazquez-Reina

1
@kindall: đừng quên các mô-đun: chúng cũng có tên chính tắc. Ngoài ra, đừng quên rằng __name__luôn luôn có thể được sửa đổi, có nghĩa là tên "chính tắc" có thể không phải là tên có thể được sử dụng để lấy đối tượng (ví dụ: khi tạo các lớp động).
nneonneo

8
Tuyên bố của bạn "đơn giản là không thể" là Sai, như @ scohe001 đã chỉ ra . Cơ sở dữ liệu tên biến của Python cũng giống như bất kỳ DB quan hệ nào khác, bạn luôn có thể tìm kiếm các đối tượng liên quan trong "đảo ngược" và trả về tìm thấy đầu tiên hoặc toàn bộ tập hợp các tên biến hợp lệ cho bất kỳ biến nào.
hobs

3
@hobs Bạn đúng kỹ thuật ... loại đúng nhất. Tuy nhiên, trên thực tế, có rất nhiều tên tiềm năng cho một đối tượng mà nó rắc rối hơn đáng để thử lấy chúng.
loại

1
@kindall Tôi đoán bạn đúng nếu ngưỡng "đáng giá" của bạn là O (1). Vòng lặp của scohe001 sẽ là O (N).
hobs

82

Ngay cả khi các giá trị biến không quay lại tên, bạn vẫn có quyền truy cập vào danh sách mọi biến được gán và giá trị của nó, vì vậy tôi rất ngạc nhiên khi chỉ có một người đề nghị lặp qua đó để tìm tên var của bạn.

Có người nói về câu trả lời đó mà bạn có thể phải đi bộ người dân địa phương và globals ngăn xếp và kiểm tra của tất cả mọi người để tìm foo, nhưng nếu foođược gán trong phạm vi mà bạn đang gọi điện thoại này retrieve_namehoạt động, bạn có thể sử dụng inspectcurrent frameđể giúp bạn có được tất cả những biến cục bộ .

Lời giải thích của tôi có thể hơi quá lời (có lẽ tôi nên sử dụng ít từ "foo" hơn), nhưng đây là cách nó sẽ tìm trong mã ( Lưu ý rằng nếu có nhiều hơn một biến được gán cho cùng một giá trị, bạn sẽ lấy cả hai tên biến đó ):

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)

Nếu bạn đang gọi chức năng này từ một chức năng khác, đại loại như:

def foo(bar):
    return retrieve_name(bar)

foo(baz)

Và bạn muốn bazthay vì bar, bạn sẽ chỉ cần quay lại một phạm vi xa hơn. Điều này có thể được thực hiện bằng cách thêm một phần bổ sung .f_backtrong phần caller_local_varskhởi tạo.

Xem một ví dụ ở đây: ideone


1
@theodox Tôi hoàn toàn đồng ý, vì điều này có thể sẽ đóng vai trò ra import hooks, ironpythonjython
scohe001

5
Điều này sẽ không làm việc. các biến nguyên tử không có tên của chúng như là một thuộc tính. Vì vậy, nếu bạn có a, b = 2, 2, retrieve_name(a)retrieve_name(b)cả hai sẽ quay lại ['a', 'b']hoặc['b', 'a']
tomas

1
@tomas Trên thực tế, đó là một chi tiết triển khai tối ưu hóa cPython trong đó các số nguyên dưới 255 về cơ bản là các singletons, do đó, bất kỳ biến nào được gán các giá trị đó sẽ trả về truemột cách hiệu quả để isso sánh
Toote

1
Có một mod của cái này để lấy tên của một var đã qua không? ví dụ: def foo(bar): retrieve_name(bar)sẽ luôn trả về thanh, nhưng nếu bạn muốn foo(baz)quay lại baz) thay vì bar?
SumNeuron

1
@SumNeuron bạn chỉ cần sửa đổi dòng được gán callers_local_varstrở lại một phạm vi để nó sẽ xem xét phạm vi của bất cứ điều gì đang gọi foo. Thay đổi dòng thành inspect.currentframe().f_back.f_back.f_locals.items()(chú ý thêm f_back).
scohe001

36

Với Python 3.8, người ta có thể chỉ cần sử dụng tính năng gỡ lỗi chuỗi f:

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

Đẹp ! Và chỉ khi bạn muốn lấy tên của một tài sản thay vì một đối tượng bạn có thểf'{foo=}'.split('=')[0].split('.')[-1]
Mickael V.

30

Trên python3, hàm này sẽ có tên ngoài cùng trong ngăn xếp:

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]

Nó là hữu ích bất cứ nơi nào trên mã. Đi qua ngăn xếp đảo ngược tìm kiếm trận đấu đầu tiên.


Công việc tốt! Mặc dù tôi đã thử retrieve_name(SomeClass.some_attribute)nhưng nó không hoạt động. Bạn có thể giúp thêm về điều đó?
Nam G VU

Điều này đấu tranh với các biến boolean. Tôi kết thúc vớistop_on_error
SumNeuron

26

Tôi không tin điều này là có thể. Hãy xem xét ví dụ sau:

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

Các abtrỏ đến cùng một đối tượng, nhưng đối tượng không thể biết biến nào trỏ đến nó.


2
Chắc chắn mối quan hệ có thể được thực hiện hai chiều bằng cách mở rộng đếm tham chiếu . Câu trả lời này (và một số người khác) thậm chí cung cấp một triển khai.
Shayaan

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

Nó được sử dụng như thế này:

name(variable=variable)

12
Điều này sẽ không trả về tên của biến mong muốn, nhưng "biến". Ví dụ: sử dụng name(variable=variable)sẽ xuất ra ['variable'], và sử dụng name(variable=another_variable)sẽ không xuất ra ['another_variable']mà là ['variable'].
DalyaG

1
Trên thực tế nó hoạt động như mong đợi. Bạn chỉ cần thay cả «biến» bằng biến của mình. Nó sẽ trả về một danh sách một thành phần với một chuỗi tên của biến đầu tiên . Ví dụ: >>> a = [] >>> b = a >>> name(a=b) ['a'] >>> name(b=a) ['b']`
Alguem Meugla

8

Đây là một cách tiếp cận. Tôi sẽ không đề xuất điều này cho bất cứ điều gì quan trọng, vì nó sẽ khá dễ vỡ. Nhưng nó có thể được thực hiện.

Tạo một hàm sử dụng inspectmô-đun để tìm mã nguồn gọi nó. Sau đó, bạn có thể phân tích mã nguồn để xác định tên biến mà bạn muốn lấy. Ví dụ, đây là một hàm được gọi là autodictdanh sách các biến và trả về một tên biến ánh xạ từ điển cho các giá trị của chúng. Ví dụ:

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

Sẽ cho:

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

Kiểm tra việc mã nguồn của nó là tốt hơn so với tìm kiếm thông qua các locals()hoặc globals()vì tiếp cận thứ hai không cho bạn biết đó của các biến là những người bạn muốn.

Ở bất cứ giá nào, đây là mã:

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

Hành động xảy ra trong dòng với inspect.getouterframes, trả về chuỗi trong mã được gọi autodict.

Nhược điểm rõ ràng của loại phép thuật này là nó đưa ra các giả định về cách cấu trúc mã nguồn. Và tất nhiên, nó sẽ không hoạt động nếu nó chạy bên trong trình thông dịch.


Xin chào, tôi chỉ có ba câu hỏi: 1. Tại sao "1"? 2. Tại sao "4"? 3. Tại sao "0"? :)
Piotr Wasilewicz

7

Tôi đã viết gói phù thủy để thực hiện loại phép thuật này một cách mạnh mẽ. Bạn có thể viết:

from sorcery import dict_of

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

và chuyển nó đến hàm tạo dataframe. Nó tương đương với:

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

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

Nếu bạn muốn viết hàm riêng của mình, có thể thực hiện sao cho bạn có thể kiểm tra một biến được xác định theo địa phương sau đó kiểm tra toàn cục. Nếu không tìm thấy gì, bạn có thể so sánh trên id () để xem biến có trỏ đến cùng một vị trí trong bộ nhớ hay không.

Nếu biến của bạn nằm trong một lớp, bạn có thể sử dụng className. dict .keys () hoặc vars (self) để xem biến của bạn đã được xác định chưa.


Nếu tên trong khung người gọi thì sao? Sau đó, bạn sẽ phải làm những điều ngớ ngẩn như đi bộ và kiểm tra mọi người localsglobals... và bạn có nguy cơ bị nhầm tên nếu không thực sự tồn tại. Đó là một tấn công việc không có lợi ích thực sự.
nneonneo

toàn bộ câu hỏi là ngớ ngẩn ... nhưng nếu đó là điều anh ấy muốn làm, thì có thể. Theo như kiểm tra sự tồn tại, bạn có thể sử dụng Globals (). Setdefault (var, <new object of type (var)) để tạo ra thứ gì đó khi không có gì ở đó. Tôi không chắc chắn chính xác những gì anh ấy muốn nó, nhưng có lẽ khi biết rằng các biến được giữ theo phạm vi, anh ấy có thể tìm ra thứ gì đó tốt hơn.
blakev

3

Trong Python, defclass từ khóa sẽ liên kết một tên cụ thể với đối tượng mà chúng xác định (hàm hoặc lớp). Tương tự, các mô-đun được đặt tên theo nguyên tắc được gọi là một cái gì đó cụ thể trong hệ thống tập tin. Trong cả ba trường hợp, có một cách rõ ràng để gán tên "chính tắc" cho đối tượng được đề cập.

Tuy nhiên, đối với các loại đối tượng khác, một tên chính tắc như vậy có thể đơn giản là không tồn tại . Ví dụ, hãy xem xét các yếu tố của một danh sách. Các yếu tố trong danh sách không được đặt tên riêng lẻ và hoàn toàn có thể là cách duy nhất để đề cập đến chúng trong một chương trình là sử dụng các chỉ mục danh sách trong danh sách chứa. Nếu danh sách các đối tượng như vậy được truyền vào hàm của bạn, bạn không thể gán các định danh có ý nghĩa cho các giá trị.

Python không lưu tên ở phía bên trái của một bài tập vào đối tượng được gán bởi vì:

  1. Nó sẽ yêu cầu tìm ra tên nào là "chính tắc" trong số nhiều đối tượng xung đột,
  2. Nó sẽ không có ý nghĩa đối với các đối tượng không bao giờ được gán cho một tên biến rõ ràng,
  3. Nó sẽ cực kỳ không hiệu quả,
  4. Theo nghĩa đen, không có ngôn ngữ khác trong sự tồn tại làm điều đó.

Vì vậy, ví dụ, các hàm được xác định bằng cách sử dụng lambdasẽ luôn có "tên" <lambda>, thay vì tên hàm cụ thể.

Cách tiếp cận tốt nhất sẽ chỉ đơn giản là yêu cầu người gọi chuyển qua một danh sách tên (tùy chọn). Nếu gõ '...','...'quá rườm rà, bạn có thể chấp nhận, ví dụ: một chuỗi chứa danh sách tên được phân tách bằng dấu phẩy (giống như namedtuple).


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

localals () - Trả về một từ điển chứa các biến cục bộ của phạm vi hiện tại. bằng cách lặp qua từ điển này, chúng ta có thể kiểm tra khóa có giá trị bằng với biến đã xác định, chỉ cần trích xuất khóa sẽ cung cấp cho chúng ta văn bản của biến ở định dạng chuỗi.

từ (sau một chút thay đổi) https://www.tutorialspoint.com/How-to-get-a-variable-name-as-a-opes-in-Python


2

Tôi nghĩ thật khó để làm điều này trong Python vì thực tế đơn giản là bạn sẽ không bao giờ biết tên của biến bạn đang sử dụng. Vì vậy, trong ví dụ của anh ấy, bạn có thể làm:

Thay vì:

list_of_dicts = [n_jobs, users, queues, priorities]

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

2

chỉ là một cách khác để làm điều này dựa trên nội dung của biến đầu vào:

(nó trả về tên của biến đầu tiên khớp với biến đầu vào, nếu không thì không. Người ta có thể sửa đổi nó để lấy tất cả các tên biến có cùng nội dung với biến đầu vào)

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

Hàm này sẽ in tên biến với giá trị của nó:

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

Tôi có một phương pháp, và trong khi không hiệu quả nhất ... nó hoạt động! (và nó không liên quan đến bất kỳ mô-đun ưa thích nào).

Về cơ bản, nó so sánh ID biến của bạn với ID toàn cầu () Biến , sau đó trả về tên của trận đấu.

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

Nếu mục tiêu là giúp bạn theo dõi các biến của mình, bạn có thể viết một hàm đơn giản gắn nhãn cho biến đó và trả về giá trị và kiểu của nó. Ví dụ: giả sử i_f = 3,01 và bạn làm tròn nó thành một số nguyên có tên i_n để sử dụng trong mã, sau đó cần một chuỗi i_s sẽ đi vào báo cáo.

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)

Điều này in ra cửa sổ tại mỗi cuộc gọi cho mục đích gỡ lỗi và cũng mang lại một chuỗi cho báo cáo bằng văn bản. Nhược điểm duy nhất là bạn phải gõ biến hai lần mỗi lần bạn gọi hàm.

Tôi là một người mới sử dụng Python và tìm thấy cách rất hữu ích này để ghi lại những nỗ lực của tôi khi tôi lập trình và cố gắng đối phó với tất cả các đối tượng trong Python. Một lỗ hổng là whatis () thất bại nếu nó gọi một hàm được mô tả bên ngoài thủ tục nơi nó được sử dụng. Ví dụ, int (i_f) là một lệnh gọi hàm hợp lệ chỉ vì hàm int được Python biết. Bạn có thể gọi whatis () bằng int (i_f ** 2), nhưng nếu vì một lý do lạ nào đó, bạn chọn xác định hàm gọi là int_squared thì nó phải được khai báo bên trong thủ tục sử dụng whatis ().


1

Có lẽ điều này có thể hữu ích:

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

Hàm đi qua danh sách ID của các giá trị từ phạm vi toàn cục (không gian tên có thể được chỉnh sửa), tìm chỉ mục của var hoặc hàm mong muốn dựa trên ID của nó và sau đó trả về tên từ danh sách tên toàn cầu dựa trên trên chỉ số có được.


1

Phương thức sau sẽ không trả về tên của biến nhưng sử dụng phương pháp này bạn có thể dễ dàng tạo khung dữ liệu nếu biến có sẵn trong phạm vi toàn cầu.

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

Đối với các hằng số, bạn có thể sử dụng một enum, hỗ trợ lấy tên của nó.


0

Tôi cố gắng lấy tên từ kiểm tra người dân địa phương, nhưng nó không thể xử lý var like a [1], b.val. Sau đó, tôi có một ý tưởng mới --- lấy tên var từ mã, và tôi thử nó thành công! mã như dưới đây:

#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

Bạn có thể thử cách sau để lấy tên của hàm bạn đã xác định (mặc dù không hoạt động đối với các hàm tích hợp):

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.