关于python函数/方法调用有什么静态的吗?

时间:2011-01-20 12:27:34

标签: python binding cpython dynamic-binding

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()

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

5 个答案:

答案 0 :(得分:7)

它们并不完全相同,但它们都是字典查找,可以用反汇编程序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        

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:2)

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

语言本身并不是说两者是相同的,因此直接属性访问可能会以某种方式进行优化。然而,Python的动态特性使得很难一直这样做,因此在CPython中,两者实际上是完全相同的。

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

答案 3 :(得分:1)

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

  

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

Python language reference 7.7: Class Definitions

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

答案 4 :(得分:1)

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

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