在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()
是否所有调用都被视为字典中的字符串查找?或者这两个例子实际上是相同的吗?
答案 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