【问题标题】:Accessing the name that an object being created is assigned to访问分配给正在创建的对象的名称
【发布时间】:2011-04-14 06:34:07
【问题描述】:

我正在编写一些代码来确定分配给对象的名称。这是用于一般调试工作并进一步熟悉 python 内部结构。

我将其结构化为类装饰器,以便在可能的情况下,该类的所有实例都将记录其名称。代码相当长,所以除非被问到,否则我不会发布它。一般技术如下

  1. 用代码装饰类的__init__ 方法来做我想做的事

  2. 设置caller = inspect.currentframe().f_back并打开inspect.getframeinfo(caller).filename并将其发送到ast.parse。我在这里不做任何错误检查,因为(1)这只是为了调试/分析/黑客(2)这个确切的过程“刚刚”完成,或者代码不会运行。这有问题吗?

  3. 找到导致当前正在执行的__init__方法运行的ast.Assignment实例

  4. 如果len(assignment.targets) == 1 那么左边只有一项,我可以从targets[0].id 中得到名字。在像a = Foo() 这样的简单形式中,assignment.valueast.Call 的一个实例。如果它是文字(例如列表),那么 value 将是该列表并保释,因为我感兴趣的对象没有被分配给名称。

确认assignment.value.func 实际上是我感兴趣的对象的type(obj).__call__ 的最佳方法是什么。我很确定我可以保证它“在某处”或代码甚至不会运行。我只需要它处于最高水平。显而易见的事情是遍历它并确保它不包含任何内部调用。然后我保证我有这个名字。 (我的推理是正确的,我不确定它的假设是否正确)。这并不理想,因为如果我对Foo 感兴趣,这可能会导致我放弃a = Foo(Bar()),因为我不知道它是否是a = Bar(Foo())

当然,我可以检查assignment.value.func.id,但有人可能已经完成了Foobar = Foo 或其他操作,所以我不想过分依赖它

任何帮助将不胜感激。与往常一样,我对我可能忽略的任何其他建议或问题感兴趣。

另外,我真的很惊讶我不得不发明“python-internals”标签。

【问题讨论】:

  • +many -- Python 很酷!

标签: python python-internals


【解决方案1】:

AST 不能给你那个答案。尝试使用 frame.f_lasti,然后查看字节码。如果下一行不是 STORE_FAST,则您有内部呼叫或其他 除了您正在寻找的简单任务之外,还继续进行。

def f():
  f = sys._getframe()
  i = f.f_lasti + 3   # capture current point of execution, advance to expected store
  print dis.disco(f.f_code, i)

【讨论】:

    【解决方案2】:

    我不知道这有多大帮助,但您是否考虑过拨打locals()?它返回一个dict,其中包含所有局部变量的名称和值。

    例如:

    s = ''
    locals()
    >>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', '__name__': '__main__', '__doc__': None}
    t = s  # I think this is what is of most importance to you
    locals()
    >>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', 't': '', '__name__': '__main__', '__doc__': None}
    

    因此您可以遍历此字典并检查哪些变量(作为它们的值)具有您要查找的类型的对象。

    就像我说的,我不知道这个答案有多大帮助,但如果您需要澄清任何事情,请发表评论,我会尽力回复。

    【讨论】:

    • 这不起作用,因为locals() 总是指它被调用的框架,我正在寻找一个框架。我可以通过sys._getframe()inspect.currentfrmae 得到它。问题是foo = bar() 不会在locals() 中创建条目(指分配发生在的框架),直到之后 bar.__init__() 返回。但这是获得名称的合适位置,因为我可以在 bar 上使用装饰器来完成它,而不是在 每个 分配之后添加代码。
    • @AaronMcSmooth:调用 globals() 而不是 locals 会解决这个问题吗
    • @ InspectoG4det 否,原因相同。 globals 只是最外层框架的frame.f_locals,因此在内部框架(在这种情况下为bar.__init__())返回之前,不会在frame.f_locals 中放置条目。
    • @AaronMcSmooth:非常感谢。其实我以前并不知道这一点。感谢您教我一些新东西 (+1)。
    【解决方案3】:

    我在这里不做任何错误检查,因为 (1) 这只是用于调试/分析/黑客攻击 (2) 这个确切的过程“刚刚”完成或者代码不会运行。这有问题吗?

    是的:

    1. 启动程序

    2. 等待它导入特定模块 foo.py 的单元

    3. 编辑 foo.py

    现在,在 Python 进程中加载​​的代码与磁盘上的代码不匹配。

    反汇编字节码可能是一种更好的技术的另一个原因。

    【讨论】:

      【解决方案4】:

      这是如何完成的。非常感谢匿名线索提供者。在为您的 alt 帐户赢得声望的过程中非常幸运。

      import inspect
      import opcode
      
      
      def get_name(f):
          """Gets the name that the return value of a function is
          assigned to. 
      
          This could be modified for classes as well. This is a
          basic version for illustration that only prints out
          the assignment instead of trying to do anything with it.
          A more flexible way would be to pass it a callback for when
          it identified an assignment.
      
          It does nothing for assignment to attributes. The solution
          for that isn't much more complicated though. If the
          instruction after the function call is a a `LOAD_GLOBAL`,
          `LOAD_FAST` or `LOAD_DEREF`, then it should be followed by
          a chain of `LOAD_ATTR`'s. The last one is the attribute
          assigned to.
          """
      
          def inner(*args, **kwargs):
              name = None
      
              frame = inspect.currentframe().f_back
              i = frame.f_lasti + 3
      
              # get the name if it exists
              code = frame.f_code
              instr = ord(code.co_code[i])
              arg = ord(code.co_code[i+1]) # no extended arg here.
              if instr == opcode.opmap['STORE_FAST']:
                  name = code.co_varnames[arg]
              elif instr in (opcode.opmap['STORE_GLOBAL'],
                             opcode.opmap['STORE_NAME']):
                  name = code.co_names[arg]
              elif instr == opcode.opmap['STORE_DEREF']:
                  try:
                      name = code.co_cellvars[arg]
                  except IndexError:
                      name = code.co_freevars[arg - len(code.co_cellvars)]
              ret = f(*args, **kwargs)
              print opcode.opname[instr]
              if name:
                  print "{0} = {1}".format(name, ret)
              return ret
      
          return inner
      
      
      @get_name
      def square(x):
          return x**2
      
      def test_local():
          x = square(2)
      
      def test_deref():
          x = square(2)
          def closure():
              y = x
          return closure
      
      x = square(2)
      test_local()
      test_deref()()
      

      使用frame.f_locals 来计算list_[i] = foo() 的赋值也不难,包括i 的值。棘手的将是文字,当它作为参数传递时。这两种情况都应该是相当具有挑战性的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-01
        • 2018-09-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-20
        相关资源
        最近更新 更多