首页 新闻 会员 周边 捐助

为什么反编译得字节码没有看不到类中这行指令?

0
[已解决问题] 解决于 2025-08-27 14:25

from weakref import WeakKeyDictionary

class Score():
""" score should in [0,100] """

def __init__(self):
    self.score = WeakKeyDictionary()
    #self.score = {}

def __get__(self, instance, owner):
    return self.score[instance]

def __set__(self, instance, value):
    if 0 <= value <= 100:
        self.score[instance] = value
    else:
        raise ValueError("score not in [0,100]")

class Student():
print("aaaaaaaaa")

# 托管属性定义在类级别上
score1 = Score()
score2 = Score()
score3 = Score()

def __init__(self, stuid, name, score1, score2, score3):
    self.stuid = stuid
    self.name = name
    self.score1 = score1
    self.score2 = score2
    self.score3 = score3

def returnMe(self):
    return "%s, %s, %i, %i, %i" % (
        self.stuid,
        self.name,
        self.score1,
        self.score2,
        self.score3)

stu = Student("20101120", "malong", 67, 77, 88)
print(stu.returnMe())

现在有个疑问:
class Student():
print("aaaaaaaaa")
这个代码print执行,会在stu对象创建之前就执行了,为什么反编译看到得字节码没有这行指令?

问题补充:

模块代码对象 就是 compile(source, filename, mode='exec') 返回的那个 PyCodeObject 结构,不仅仅模块代码对象
help(compile)可以看到source可以module(模块),statement(语句),表达式(expression)

_java_python的主页 _java_python | 小虾三级 | 园豆:828
提问于:2025-08-27 14:09
< >
分享
最佳答案
0

原因:
print("aaaaaaaaa") 写在 类体作用域,它在 类定义阶段(即 class Student: 这一行被执行时)就被 立即运行,而不是等到 stu = Student(...) 创建实例时

反编译类体字节码
import dis
dis.dis(Student)
你会看到:
复制
0 LOAD_GLOBAL              0 (print)
2 LOAD_CONST               1 ('aaaaaaaaa')
4 CALL_FUNCTION            1
6 POP_TOP
...
这几条指令就在 类体字节码的最前面,随着 class Student: 的执行立刻跑完

执行时机
模块加载 或 交互式输入 时,解释器遇到 class Student: 就会:
① 执行类体里的表达式/语句(包括 print);
② 生成类对象并绑定到名字 Student。
后面 stu = Student(...) 只触发 init 和实例创建,不会再次执行类体里的 print

它确实属于 模块代码对象 的一部分,只是被 嵌套在类体字节码 里,而不是游离在模块顶层。
把过程拆开就清楚了:
整个文件最先被编译成一个 模块代码对象(PyCodeObject)。
在这个模块的 co_consts 里,每一个类体也被编译成一个独立的 PyCodeObject(类体字节码)。
当模块字节码执行到 class Student: 这一行时,会执行:

LOAD_BUILD_CLASS        # 拿到 __build_class__
LOAD_CONST  <类体代码对象>
MAKE_FUNCTION 0
LOAD_CONST  'Student'
CALL_FUNCTION 3         # 真正执行类体字节码
STORE_NAME  Student

CALL_FUNCTION 3 这一步会把“类体代码对象”当作函数一样执行,于是类体里的 print("aaaaaaaaa") 立即运行,但仍在模块主线程里完成。
从“字节码”视角:这段 print 位于 类体子代码对象,不在模块顶层指令流。

LOAD_CONST <类体代码对象>
把类体那段字节码(PyCodeObject)压栈。
MAKE_FUNCTION 0
把这段字节码包装成 可调用函数对象(等价于 lambda: <类体>)。
接下来再压 3 个实参:
函数对象(刚才 MAKE_FUNCTION 得到的)
字符串 'Student'(类名)
基类元组(() 或 (Base,))
CALL_FUNCTION 3
真正调用 build_class(func, 'Student', bases),
其中 func 就是第 2 步生成的“类体函数”,
build_class 内部执行 func() 完成类体代码(包括 print("aaaaaaaaa"))并返回新类对象。


要获取类体代码对象,用模块中的常量表中获取:
可以用compile获取模块代码对象,然后通过模块代码对象中的常量表获取类体代码对象
不确定索引,先打印所有的常量表
class_code = module_code.co_consts[1]
然后再反编译类体代码对象,可以看到
print(dis.dis(class_code)),
“类体代码对象”只包含类体里的可执行语句(print(...)、x = 1、def foo(...) 等),
而 “类定义” 这个动作 是由外部代码(build_class)把这份代码对象当成 函数 来执行,最终返回一个 新的类对象。
一句话:
类体代码对象 = 待运行的函数体;
类对象 = 运行这份函数体后得到的产物。
反编译类体代码对象可以看到开头这几行,Python 编译器自动为每一个类体生成的“前置初始化指令”,固定模板,非类定义,类体代码对象不包含类定义。print(dis.dis(module_code.co_consts[1])):
LOAD_NAME 0 (name)
STORE_NAME 1 (module)
LOAD_CONST 0 ('Student')
STORE_NAME 2 (qualname)
...
和直接compile('类体','<string>','exec')注意类体不包含类定义,因为类体是缩进的,执行compile命令报错,直接将类体所有缩进去掉,可以看到,和上面的通过模块的常量表中获取的类代码对象一样,在方法中加了个Student,我猜是不是就是上面头部四句中__qualname__带上的

_java_python | 小虾三级 |园豆:828 | 2025-08-27 14:12
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册