【问题标题】:variable name introspection in PythonPython中的变量名自省
【发布时间】:2009-12-12 20:23:10
【问题描述】:

是否可以在 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__ 之类的东西来减少,而不是分别显式地重复“创建”和“删除”。 (我认为可以为此使用装饰器,但这似乎有点矫枉过正。)

【问题讨论】:

    标签: python metaprogramming


    【解决方案1】:

    在一般情况下,您不能从一个值中推断出名称(可能没有名称,可能有多个名称,等等);当您调用您假设的 makedict(name) 时,namemakedict 收到的,因此(再次,在一般情况下)它无法辨别值的名称(如果有的话)从。你可以检查你的调用者的命名空间,看看你是否有幸遇到一个特殊情况,该值确实可以让你推断名称(例如,你收到23,并且在感兴趣的命名空间中只有一个名称恰好发生在具有23!) 的值,但这显然是一个脆弱且不稳定的架构。另外,在您的第一个示例案例中,绝对保证特殊情况不会发生——name 中的值与foobaz 中的值完全相同,所以它是100% 确定该值的名称绝对是模棱两可的。

    您可以采取完全不同的策略,例如调用makedict('name type', locals())(明确地传递locals() 可能会被黑暗和深入的内省魔法所避免,但这通常不是最可靠的选择)- 传递 names(还有命名空间,我建议!)并让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 提供了许多替代方式来实现这种实现。

    【讨论】:

    • 谢谢,这很有见地。我预计不会有简单的解决方案,并最终同意这不值得麻烦。
    【解决方案2】:

    一般来说,你不能在 Python 中这样做,因为 Python 有对象,而不是变量。

    如果我有

    L1 = [1,2]
    L2 = L1
    

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

    同样:

    def f1(): pass
    f2 = f1
    

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

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

    【讨论】:

    • 是的,我已经意识到了这一点,但我认为可能有一些方法可以解决这个问题。谢谢!
    【解决方案3】:

    你可以为函数做你想做的事:

    >>> def blagnorf():
        pass
    
    >>> print blagnorf.__name__
    blagnorf
    

    但不幸的是,不适用于变量。你可以为你的 python 代码编写一个预处理器来为你做这件事,虽然......

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

    【讨论】:

    • 这仅适用于各自的功能之外。还是谢谢。
    • 您可以从函数内部获取堆栈跟踪并获取对正在执行的当前函数的引用。或者您也可以在函数内执行“blagnorf.__name__”。
    【解决方案4】:

    每当您渴望创建无数简单方法,其内部结构仅根据方法的名称而有所不同时,您可能应该重新考虑您的接口。相反,您可以这样做:

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

    其中操作可以是“创建”、“删除”等。

    【讨论】:

    • 我同意 - 不幸的是,我无法控制这里的界面。
    猜你喜欢
    • 1970-01-01
    • 2019-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-07
    • 1970-01-01
    相关资源
    最近更新 更多