Python的执行顺序是“自上而下、逐行执行”,但要结合具体语句的类型(如定义、表达式、导入、类定义等)来理解。
简单例子讲解
test5.py
print("test5module: start executing")
x = 10
def func():
print("test5module: func called")
class MyModClass:
print("test5module: MyModClass body executing")
def __init__(self):
print("test5module: MyModClass __init__")
print("test5module: end executing")
test.py
print("main: script start")
import sys # 标准库
import test5 # 导入自定义模块
from test5 import func # 只导入mymodule中的func
import math as m # 标准库,取别名
try:
import numpy as np # 第三方库(如果有安装)
except ImportError:
np = None
print("main: after imports")
class Demo:
print("Demo class: class body executing")
# 类变量
class_var = 100
# 魔术方法
def __new__(cls, *args, **kwargs):
print("Demo.__new__ called")
instance = super().__new__(cls)
return instance
def __init__(self, value):
print("Demo.__init__ called")
self.value = value # 实例变量
# 实例方法
def instance_method(self):
print("Demo.instance_method: value =", self.value)
# 类方法
@classmethod
def class_method(cls):
print("Demo.class_method: class_var =", cls.class_var)
# 静态方法
@staticmethod
def static_method():
print("Demo.static_method called")
# 魔术方法
def __call__(self, x):
print(f"Demo.__call__({x}) called")
print("main: after class definition")
def main():
print("main: in main() function")
# 实例化对象
d = Demo(42)
d.instance_method()
d.class_method()
Demo.class_method()
d.static_method()
Demo.static_method()
# 使用魔术方法
d(99) # 调用__call__
# 访问类变量和实例变量
print("d.value =", d.value)
print("Demo.class_var =", Demo.class_var)
# 使用导入的模块/函数
func()
print("math.sqrt(16) =", m.sqrt(16))
if np:
print("np.arange(3) =", np.arange(3))
if __name__ == "__main__":
print("main: __name__ == '__main__'")
main()
一、详细剖析执行顺序与原理
1. 脚本自上而下执行
print(“main: script start”) 立即输出。
import sys:标准库,导入后可用,顶层无输出。
import mymodule:首次导入,解释器进入
mymodule.py
,顶层代码逐行执行,输出如下:mymodule: start executing mymodule: MyModClass body executing mymodule: end executing
from mymodule import func:模块已导入,不重复执行顶层代码,只是把func指向主程序空间。
import math as m:标准库导入,顶层无输出。
import numpy as np:如果有安装则导入,否则np=None。
print(“main: after imports”) 输出。
2. 类定义阶段(遇到 class Demo)
- print(“Demo class: class body executing”) 立即输出。
- class_var绑定到类对象。
- 方法、魔术方法等,只是函数对象被绑定到类命名空间,不会执行方法体。
- 类定义结束,Demo名字指向新创建的类对象。
3. print(“main: after class definition”) 输出。
4. def main():定义main函数,仅绑定,不执行。
5. if name == “main”: 判断通过,进入分支,输出 main: __name__ == '__main__'
,然后执行main()
。
6. main()函数内依次执行:
- 输出
main: in main() function
- d = Demo(42)
- 首先调用
Demo.__new__
(输出Demo.__new__ called
) - 然后调用
Demo.__init__
(输出Demo.__init__ called
)
- 首先调用
- d.instance_method() 输出
Demo.instance_method: value = 42
- d.class_method() 和 Demo.class_method() 都输出
Demo.class_method: class_var = 100
- d.static_method() 和 Demo.static_method() 都输出
Demo.static_method called
- d(99) 调用
Demo.__call__
,输出Demo.__call__(99) called
- 打印变量:输出
d.value = 42
、Demo.class_var = 100
- func() 执行,输出
mymodule: func called
- math.sqrt(16) 计算输出
math.sqrt(16) = 4.0
- np.arange(3) 如果numpy可用,输出
np.arange(3) = [0 1 2]
7. 完整输出顺序(假设有numpy)
main: script start
mymodule: start executing
mymodule: MyModClass body executing
mymodule: end executing
main: after imports
Demo class: class body executing
main: after class definition
main: __name__ == '__main__'
main: in main() function
Demo.__new__ called
Demo.__init__ called
Demo.instance_method: value = 42
Demo.class_method: class_var = 100
Demo.class_method: class_var = 100
Demo.static_method called
Demo.static_method called
Demo.__call__(99) called
d.value = 42
Demo.class_var = 100
mymodule: func called
math.sqrt(16) = 4.0
np.arange(3) = [0 1 2]
二、进一步说明
1. 类体内哪些会立即执行?
- 类体的所有顶层语句都会在类定义时立即执行,如
print
、表达式、赋值等。 - def定义、@classmethod、@staticmethod等只是创建并注册方法,不会调用方法体。
- 魔术方法和普通方法定义过程和普通def一样。
2. import的多种形式区别
- import moduleA:导入整个模块对象,需要moduleA.xxx调用。
- from moduleA import x, y:把x, y直接引入当前命名空间。
- import moduleA as ma:给模块取别名。
- import third-party:原理同上,只是模块来源不同。首次导入时会执行其顶层代码。
3. if name == “main” 的作用
- 只有脚本直接运行时才执行main函数,被其他模块import时不会执行main。
三、总结
- Python解释器自上而下逐行执行所有顶层语句。
- def和class只是创建对象并绑定名字,def体不会执行,class体会执行(如顶层print等)。
- import语句会立即导入模块,首次导入会执行模块顶层代码。
- 类中方法/变量/魔术方法的定义过程就是函数对象绑定到类命名空间,不会被执行,只有实例化或调用时才会自动触发。
- **if name == “main”**常用于脚本入口保护。
非常感谢你的反馈!下面我将结合前述讲解和你的新问题,用更清晰的例子和Markdown图解,详细解释:
- 什么是类命名空间?
- 类体执行时,赋值/def到底存在哪?
- 类对象如何生成?它和命名空间的关系是什么?
- __new__的作用与流程
- 如何通过代码和图解理解整个过程?
进一步例子讲解–例子更贴合实际(无父类版本)
一、类定义的底层过程
假设你有如下代码:
class Demo:
class_var = 10 # 类变量
def __new__(cls, *args, **kwargs):
print("Demo.__new__ called")
instance = super().__new__(cls)
return instance
def __init__(self, value):
print("Demo.__init__ called")
self.instance_var = value
def foo(self):
print("Demo.foo called")
1.1 解释器如何处理class体?
a) 进入class体时,解释器会创建一个新的局部命名空间(字典),称为“类命名空间”。
- 所有赋值(如
class_var = 10
) - 所有def语句(如
def __init__...
,会创建函数对象,然后以名字为key保存到命名空间)
b) 类体执行期间,所有内容都存在类命名空间中
# 类体执行到最后,类命名空间长这样(伪代码):
Demo命名空间 = {
'class_var': 10,
'__new__': <function object>,
'__init__': <function object>,
'foo': <function object>
}
c) 类定义结束时,解释器会创建类对象,并将类命名空间的内容作为属性绑定到这个类对象上
Demo类对象
├── class_var : 10
├── __new__ : <function object>
├── __init__ : <function object>
├── foo : <function object>
d) 关系总结
- 类命名空间:class体执行时的“临时字典”,用于收集类体定义的所有名字。
- 类对象:class体执行完毕后,由解释器自动生成的对象。类对象的属性就是类命名空间的内容。
二、流程详细图
2.1 类定义时的流程
1. 解释器"遇见" class Demo:
|
|---> 创建一个"类命名空间"(空字典)
|
|---> 依次执行class体每行代码,每一行的赋值/def都写入这个字典
| ├─ class_var = 10 -> class_var: 10
| ├─ def __new__(...) -> __new__: function object
| ├─ def __init__(...) -> __init__: function object
| └─ def foo(...) -> foo: function object
|
|---> class体执行完毕
|
|---> 解释器用这个字典,创建一个新的"类对象"
| └─ 类对象的属性 = 类命名空间的内容
|
|---> "Demo"这个名字指向这个类对象
2.2 类对象、实例对象的结构图
Demo (类对象)
├── class_var = 10
├── __new__ # 方法
├── __init__ # 方法
├── foo # 方法
e = Demo(123) # 实例化后
e (Demo实例对象)
├── instance_var = 123 # 只属于e的实例变量
三、实例化的详细过程(含 new)
e = Demo(123)
- 实例化过程:
- 先调用
Demo.__new__
(类的__new__方法)- 用于分配内存,创建一个空对象
- 通常返回一个实例对象
- 再调用
Demo.__init__
- 用于初始化刚创建的实例
- 通过
self.instance_var = value
给实例绑定实例变量
- 先调用
实例化流程图
e = Demo(123)
|
|---> Demo.__new__(Demo, 123)
| |
| |---> super().__new__(Demo) # 创建空对象 e
| |---> 返回 e
|
|---> Demo.__init__(e, 123)
| |
| |---> e.instance_var = 123 # 给e绑定实例变量
四、代码验证与对比
class Demo:
class_var = 10
def __new__(cls, *args, **kwargs):
print("Demo.__new__ called")
instance = super().__new__(cls)
return instance
def __init__(self, value):
print("Demo.__init__ called")
self.instance_var = value
def foo(self): pass
print('类命名空间内容:')
print(Demo.__dict__) # 类对象的属性,实际就是类命名空间内容
e = Demo(123)
print('实例命名空间内容:')
print(e.__dict__)
输出(部分):
类命名空间内容:
{'__module__': '__main__', 'class_var': 10, '__new__': <function>, '__init__': <function>, 'foo': <function>, ...}
Demo.__new__ called
Demo.__init__ called
实例命名空间内容:
{'instance_var': 123}
五、总结要点
- 类体执行时,所有赋值和def都写入类命名空间(一个临时字典)。
- 类定义结束后,解释器用这个字典作为属性,创建类对象。
- 类对象的属性(方法、类变量)就是类命名空间的内容。
- 实例变量只在实例化(如__init__)时,通过
self.xxx=yyy
绑定到实例对象上。 __new__
用于创建实例对象,__init__
用于初始化实例变量。
最后,完整结构图
这里补充一个“实例修改类”(这个话是一个错误的话)
因为修改class_var
这会在 e 的实例字典 e.__dict__
里新建一个名为class_var的实例变量,它只属于 e。
从此,e.class_var 访问的是实例自己的,不再是类的那个。
其他实例和类本身的 class_var 不受影响。再次声明,以防出现误解。
Demo (类对象)
├── class_var = 10
├── __new__ # 方法
├── __init__ # 方法
├── foo # 方法
e = Demo(123) # 实例化后
├── instance_var = 123 # 只属于e
└── class_var = 20 # 只属于e(实例变量,遮盖了类变量)
f = Demo(456) # 另一个实例
└── instance_var = 456 # 只属于f
Demo (类对象)
└── class_var = 10 # 仍然属于类对象
查找变量/方法顺序:
实例对象 → 类对象 → 父类对象
关于有父类的
1. 复杂多层继承示例代码
场景:公司系统中,不同员工类型有不同的属性和行为
class Person:
species = "Homo sapiens"
def __new__(cls, *args, **kwargs):
print(f"Person.__new__ for {cls.__name__}")
return super().__new__(cls)
def __init__(self, name):
print("Person.__init__")
self.name = name
def introduce(self):
print(f"I am {self.name}, a {self.species}")
class Employee(Person):
company = "PyTech"
def __init__(self, name, emp_id):
print("Employee.__init__")
super().__init__(name)
self.emp_id = emp_id
def work(self):
print(f"{self.name} is working at {self.company}")
class Manager(Employee):
def __init__(self, name, emp_id, team_size):
print("Manager.__init__")
super().__init__(name, emp_id)
self.team_size = team_size
def manage(self):
print(f"{self.name} manages a team of {self.team_size}")
2. 类定义与命名空间分析
2.1 类体执行时的命名空间内容
Person
类体结束后
Person命名空间:
├── species : "Homo sapiens"
├── __new__ : <function>
├── __init__ : <function>
└── introduce : <function>
Employee
类体结束后
Employee命名空间:
├── company : "PyTech"
├── __init__ : <function>
└── work : <function>
# 注意:没有species、introduce、__new__,但继承自Person
Manager
类体结束后
Manager命名空间:
├── __init__ : <function>
└── manage : <function>
# 其它成员都继承自Employee/Person
3. 实例化过程与属性查找
m = Manager("Alice", "A1001", 5)
实例化流程(含super链路)
1. Manager.__new__(继承自Person.__new__)
└─ 打印:Person.__new__ for Manager
└─ 返回新实例 m
2. Manager.__init__(m, "Alice", "A1001", 5)
└─ 打印:Manager.__init__
└─ 调用 super().__init__("Alice", "A1001")
└─ Employee.__init__(m, "Alice", "A1001")
└─ 打印:Employee.__init__
└─ 调用 super().__init__("Alice")
└─ Person.__init__(m, "Alice")
└─ 打印:Person.__init__
└─ m.name = "Alice"
└─ m.emp_id = "A1001"
└─ m.team_size = 5
3.1 实例属性分布
Manager (类对象)
├── __init__ # Manager自定义
├── manage # Manager自定义
├── company # 继承自Employee
├── work # 继承自Employee
├── species # 继承自Person
├── introduce # 继承自Person
├── __new__ # 继承自Person
m (Manager实例对象)
├── name = "Alice" # 从Person.__init__赋值
├── emp_id = "A1001" # 从Employee.__init__赋值
├── team_size = 5 # 从Manager.__init__赋值
4. 属性与方法的查找顺序图解
m.work()
- 先查找m实例→找不到
- 再查Manager类→找不到
- 再查Employee类→找到work方法
m.introduce()
- Manager类→没有
- Employee类→没有
- Person类→有,执行
m.company
- Manager类→没有
- Employee类→有,返回"PyTech"
m.species
- Manager类→没有
- Employee类→没有
- Person类→有,返回"Homo sapiens"
m.name
- 这是实例变量,在m的__dict__里
5. 结构总览(Markdown树状图)
Manager (类对象)
├── __init__ [Manager自己的]
├── manage [Manager自己的]
├── work [继承自Employee]
├── company [继承自Employee]
├── introduce [继承自Person]
├── species [继承自Person]
├── __new__ [继承自Person]
m = Manager("Alice", "A1001", 5) (实例)
├── name = "Alice" [Person.__init__]
├── emp_id = "A1001" [Employee.__init__]
├── team_size = 5 [Manager.__init__]
6. 验证代码
m = Manager("Alice", "A1001", 5)
print("实例命名空间:", m.__dict__)
print("company:", m.company)
print("species:", m.species)
m.introduce()
m.work()
m.manage()
输出:
Person.__new__ for Manager
Manager.__init__
Employee.__init__
Person.__init__
实例命名空间: {'name': 'Alice', 'emp_id': 'A1001', 'team_size': 5}
company: PyTech
species: Homo sapiens
I am Alice, a Homo sapiens
Alice is working at PyTech
Alice manages a team of 5
7. 总结
- 类体内所有变量/方法都进类命名空间,然后绑定到类对象。
- 多层继承时,子类自己的内容先绑定,父类内容通过MRO链路查找。
- 实例化时,
__new__
和__init__
沿MRO链路(super)逐级调用,分别负责分配对象和初始化实例属性。 - 实例变量只在实例的
__dict__
,类变量/方法都在类对象,继承链查找。