【问题标题】:Is there anything static about python function / method invocations?关于 python 函数/方法调用有什么静态的吗?
【发布时间】:2011-01-20 12:27:34
【问题描述】:

asking a question about reflection我问:

很好的答案。但是说myobject.foo()x = getattr(myobject, "foo"); x(); 是有区别的。哪怕只是化妆品。首先, foo() 是静态编译的。在第二种情况下,可以通过多种方式生成字符串。 – 乔 1 小时前

得到了答案:

呃,土豆/马铃薯......在python中,niether是静态编译的,所以它们或多或少是等价的。 – SWeko 1 小时前

我知道 Python 对象的成员存储在字典中并且一切都是动态的,但我假设给定以下代码:

class Thing():
  def m(self):
    pass

t = Thing()

以下代码会在生成 .pyc 时以某种方式静态编译:

t.m()

即编译器知道m()的地址,所以在运行时没有点绑定。那个或运行时会缓存后续的查找。

而这总是涉及到查字典:

meth = getattr(t, "m")
meth()

是否所有调用都被视为字典中的字符串查找?还是这两个例子实际上是相同的?

【问题讨论】:

    标签: python binding cpython dynamic-binding


    【解决方案1】:

    它们并不完全相同,但它们都是字典查找,可以使用反汇编程序 dis.dis 显示。

    特别注意LOAD_ATTR 指令通过名称动态查找属性。根据文档,它“将 TOS [栈顶​​] 替换为 getattr(TOS, co_names[namei])”。

    >>> from dis import dis
    >>> dis(lambda: t.m())
      1           0 LOAD_GLOBAL              0 (t)
                  3 LOAD_ATTR                1 (m)
                  6 CALL_FUNCTION            0
                  9 RETURN_VALUE        
    >>> dis(lambda: getattr(t, 'm')())
      1           0 LOAD_GLOBAL              0 (getattr)
                  3 LOAD_GLOBAL              1 (t)
                  6 LOAD_CONST               0 ('m')
                  9 CALL_FUNCTION            2
                 12 CALL_FUNCTION            0
                 15 RETURN_VALUE        
    

    【讨论】:

      【解决方案2】:

      所有调用都被视为字典查找(好吧,python 内部可能会进行某种优化,但就其工作方式而言,您可以假设它们是字典查找)。

      python 中没有静态编译。甚至可以这样做:

      t = Thing()
      t.m = lambda : 1
      t.m()
      

      【讨论】:

        【解决方案3】:

        这取决于您询问的是 Python 语言,还是 CPython 等特定实现。

        语言本身并没有说两者是相同的,因此直接属性访问可能会以某种方式进行优化。然而,Python 的动态特性使其难以始终如一地做到这一点,因此在 CPython 中两者实际上是相同的。

        话虽如此,直接t.m() 可能比使用getattr() 快两倍,因为它涉及一个字典查找,而不是使用getattr() 获得的两个:即全局名称getattr() 本身必须查字典。

        【讨论】:

          【解决方案4】:

          不仅类在运行时可以修改(如HS example);但即使class 关键字在运行时也是“执行”的:

          类定义是可执行文件 陈述。它首先评估 继承列表(如果存在)。每个 继承列表中的项目应该 评估为类对象或类 允许子类化的类型。这 类的套件然后在一个 新的执行框架(见第 命名和绑定),使用新的 创建本地命名空间和 原始的全局命名空间。 (通常, 该套件仅包含功能 定义。)当类的套件 完成执行,它的执行 帧被丢弃,但其本地 命名空间被保存。 [4] 一个类对象 然后使用继承创建 基类列表和 保存的本地命名空间 属性字典。班级名称 绑定到这个类对象 原始本地命名空间。

          (Python language reference 7.7: Class Definitions)

          换句话说,在启动时,解释器执行class 块内的代码并保留生成的上下文。在编译时,无法知道类的外观。

          【讨论】:

            【解决方案5】:

            使用getattr,您可以访问名称不是有效标识符的属性,但我不确定是否有使用此类属性而不是字典的用例。

            >>> setattr(t, '3', lambda : 4)
            >>> t.3()
              File "<stdin>", line 1
                t.3()
                  ^
            SyntaxError: invalid syntax
            >>> getattr(t, '3')()
            4
            

            【讨论】:

              猜你喜欢
              • 2014-04-27
              • 2021-04-10
              • 1970-01-01
              • 1970-01-01
              • 2011-01-27
              • 2016-11-16
              • 1970-01-01
              • 2013-11-12
              • 1970-01-01
              相关资源
              最近更新 更多