3

问一个关于反射的问题时,我问:

不错的答案。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()

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

4

5 回答 5

7

它们并不完全相同,但它们都是字典查找,如 disassembler 所示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        
于 2011-01-20T12:41:14.690 回答
3

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

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

t = Thing()
t.m = lambda : 1
t.m()
于 2011-01-20T12:35:27.837 回答
2

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

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

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

于 2011-01-20T13:30:21.593 回答
1

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

类定义是一个可执行语句。它首先评估继承列表(如果存在)。继承列表中的每个项目都应评估为允许子类化的类对象或类类型。然后使用新创建的本地名称空间和原始的全局名称空间在新的执行框架中执行该类的套件(请参阅命名和绑定部分)。(通常,套件只包含函数定义。)当类的套件完成执行时,它的执行帧被丢弃,但它的本地命名空间被保存。[4] 然后使用基类的继承列表和属性字典的保存的本地名称空间创建一个类对象。类名绑定到原始本地命名空间中的此类对象。

Python 语言参考 7.7:类定义

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

于 2011-01-20T12:44:44.080 回答
1

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

>>> setattr(t, '3', lambda : 4)
>>> t.3()
  File "<stdin>", line 1
    t.3()
      ^
SyntaxError: invalid syntax
>>> getattr(t, '3')()
4
于 2011-01-20T14:08:01.650 回答