python的执行顺序

发布于:2025-06-27 ⋅ 阅读:(16) ⋅ 点赞:(0)

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. 脚本自上而下执行

  1. print(“main: script start”) 立即输出。

  2. import sys:标准库,导入后可用,顶层无输出。

  3. import mymodule:首次导入,解释器进入mymodule.py,顶层代码逐行执行,输出如下:

    mymodule: start executing
    mymodule: MyModClass body executing
    mymodule: end executing
    
  4. from mymodule import func:模块已导入,不重复执行顶层代码,只是把func指向主程序空间。

  5. import math as m:标准库导入,顶层无输出。

  6. import numpy as np:如果有安装则导入,否则np=None。

  7. 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 = 42Demo.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)
  1. 实例化过程:
    1. 先调用 Demo.__new__ (类的__new__方法)
      • 用于分配内存,创建一个空对象
      • 通常返回一个实例对象
    2. 再调用 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__类变量/方法都在类对象,继承链查找。

网站公告

今日签到

点亮在社区的每一天
去签到