Python中的变量名称内省

时间:2009-12-12 20:23:11

标签: python metaprogramming

是否可以在Python中动态确定变量的名称?

例如,我有时会遇到以下情况:

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

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

如果可以通过D = makedict(name, type)等方式减少重复,那就太好了。

有些相关的是,函数有时会知道自己的名字:

class Item(object):

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

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

这里可以通过传递类似__methodname__的内容而不是分别明确重复“创建”和“删除”来减少重复。 (我假设装饰师可以用于此,但这看起来有点矫枉过正。)

4 个答案:

答案 0 :(得分:10)

在一般情况下,您不能从值中推断出名称(可能没有名称,可能有多个名称等);当您拨打假设makedict(name)时,namemakedict收到的内容,因此(再次,在一般情况下)它无法辨别出什么名称(如果有的话)价值来自。你可以自省你的调用者的命名空间,看看你是否足够幸运地遇到一个特殊情况,其值可以让你推断出名称(例如,你收到23,并且在整个感兴趣的命名空间中只有一个名称碰巧的值为23!),但这显然是一个脆弱且不确定的架构。另外,在您的第一个示例案例中,绝对保证特殊情况 - name中的值与foo或{{baz中的值完全相同1}},所以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提供了许多替代方法来实现这种实现。

答案 1 :(得分:4)

通常,你不能在Python中这样做,因为Python有对象,而不是变量。

如果我有

L1 = [1,2]
L2 = L1

然后,L1L2都指向同一个对象。它没有单个名称。

类似地:

def f1(): pass
f2 = f1

现在该函数没有单一名称,因此,您无法找到该函数的“名称”。

Python中的对象可以被许多名称引用 - 当对象的引用计数变为零时,Python会为它释放内存。

答案 2 :(得分:2)

你可以做你想要的功能:

>>> def blagnorf():
    pass

>>> print blagnorf.__name__
blagnorf

但不幸的是,不是变量。您可以为您的python代码编写一个预处理器来为您完成,但是......

请注意,您可以使用他们在那里的宏系统在Scheme / Lisp中执行此操作。

答案 3 :(得分:2)

每当你渴望制作一个简单的方法,其内部仅根据方法的名称而有所不同时,你可能应该重新考虑你的界面。相反,你可以这样做:

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

其中action可以是“create”,“delete”等。