2009-12-12 2 views
4

Возможно ли динамическое определение имени переменной в Python?Интроспекция имени переменной в Python

Например, я иногда следующая ситуация:

name = foo if bar else baz 
type = alpha or bravo 

D = { 
    "name": name, 
    "type": type 
} 

Было бы хорошо, если дублирование не может быть уменьшен с чем-то вроде D = makedict(name, type).

Несколько Relatedly, было бы иногда быть полезным для функции, чтобы знать свое собственное имя:

class Item(object): 

    def item_create(self, item): 
     dispatch("create", item) 

    def item_delete(self, item): 
     dispatch("delete", item) 

Здесь дублирование может быть уменьшен путем пропускания что-то вроде __methodname__ вместо явного повторения «создать» и «удалить», соответственно. (Я предполагаю, что для этого можно использовать декоратор, но это похоже на излишний.)

ответ

10

В общем случае вы не можете вывести имя из значения (не может быть имени, могут быть несколько и т. Д.); когда вы звоните в свою гипотетическую makedict(name), значение из name - это то, что makedict получает, поэтому (опять же, в общем случае) он не может различить, какое имя (если есть). Вы могли бы исследовать пространства имен ваших вызывающих лиц, чтобы убедиться, что вам повезло, чтобы попасть в специальный случай, когда значение позволяет вам вывести имя (например, вы получаете 23, и есть только одно имя на всех интересующих пространствах имен, которое имеет значение 23!), но это явно хрупкая и неудобная архитектура. Кроме того, в вашем первом примере случае абсолютно гарантируется, что произойдет специальный случай , а не - значение в name точно такое же, как в foo или baz, так что он на 100% уверен, что имя для этого значения будет безнадежно двусмысленный.

Вы могли бы принять совершенно другой подход, например, вызов makedict('name type', locals()) (прохождение locals() явно может быть устранен с темной и глубокой магией самоанализа, но это не самый твердый выбор в целом) - передать в имен (и пространства имен, я предлагаю!) и имеют makedict вывод значений, что, очевидно, гораздо более прочное предложение (так как каждое имя имеет ровно одно значение, но не наоборот). То есть:

def makedict(names, *namespaces): 
    d = {} 
    for n in names.split(): 
    for ns in namespaces: 
     if n in ns: 
     d[n] = ns[n] 
     break 
    else: 
     d[n] = None # or, raise an exception 

Если вы заинтересованы в выкапывания пространств имен интроспекции, а не иметь их чисто указанный абонент, посмотрите на inspect.getouterframes - но я предлагаю вам пересмотреть.

Вторая проблема, которую вы поднимаете, совсем другая (хотя вы могли бы снова использовать функции inspect, чтобы исследовать имя вызывающего абонента или собственное имя функции - какая своеобразная идея!). Что общего в двух случаях, так это то, что вы используете чрезвычайно мощный и опасный механизм для выполнения работы, которая может быть выполнена гораздо проще (проще обеспечить правильность, проще отладить любые проблемы, проще протестировать и т. Д.) - - далекие от того, чтобы декораторы были «излишними», они далеки проще и яснее, чем предполагаемая интроспекция. Если у вас есть методы мильона все формы:

def item_blah(self, item): 
    dispatch("blah", item) 

самый простой способ их создания может быть:

class Item(object): pass 

def _makedispcall(n): 
    def item_whatever(self, item): 
    dispatch(n, item) 
    item_whatever.__name__ = 'item_' + n 
    return item_whatever 

for n in 'create delete blah but wait theres more'.split(): 
    setattr(Item, 'item_' + n, _makedispcall(n)) 

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

+0

Спасибо, это очень проницательно. Я ожидал, что не будет простого решения, и в конечном итоге согласен, что это не стоит проблем. – AnC

4

В общем, вы не можете сделать это в Python, потому что у Python есть объекты, а не переменные.

Если у меня есть

L1 = [1,2] 
L2 = L1 

Затем L1 и L2 оба относятся к одному объекту. Он не имеет номер.

Аналогично:

def f1(): pass 
f2 = f1 

Теперь функция не имеет ни одного имени, и таким образом, вы не можете найти «» имя функции.

Объект в Python может ссылаться на многие имена - когда счетчик ссылок объекта обращается в нуль, Python освобождает память для него.

+0

Да, я понял, что это, но понял, что может быть какой-то способ, чтобы понять это, тем не менее , Благодаря! – AnC

1

Вы можете делать то, что вы хотите для функций:

>>> def blagnorf(): 
    pass 

>>> print blagnorf.__name__ 
blagnorf 

Но не для переменных, к сожалению. Вероятно, вы могли бы написать препроцессор для своего кода на Python, чтобы сделать это для вас, хотя ...

Обратите внимание, что вы можете сделать это в Scheme/Lisp с помощью макросистемы, которую они там есть.

+0

Это работает только за пределами соответствующей функции. Спасибо, в любом случае. – AnC

+0

Вы можете получить трассировку стека и получить ссылку на текущую функцию, выполняемую таким образом, изнутри функции. или вы также можете сделать «blagnorf .__ name__» внутри функции. – Claudiu

1

Всякий раз, когда вы получаете желание создавать простые простые методы, чья внутренность зависит только от имени метода, вы, вероятно, должны пересмотреть свой интерфейс. Вместо этого, вы можете сделать это:

class Item(object): 
    def item_action(self, action, item): 
     dispatch(action, item) 

, где действие может быть «создать», «удалить», и т.д.

+0

Я согласен - к сожалению, я не контролирую интерфейс здесь. – AnC

Смежные вопросы