DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • python面向过程

  • python面向对象

  • 网络并发编程

  • 不基础的py基础

    • 默认参数
    • 装饰器
    • Code Object
      • f.__code__
      • Code object里有什么
        • co_code
        • metadata相关
        • 运行时需要的
        • 关于输入参数的
  • 设计模式

  • python_Need
  • 不基础的py基础
DC
2024-06-21
目录

Code Object


★ 该篇博客讲一个硬核的东西 - code object 这个东西对于你理解Python的底层机制非常重要!

# f.__code__

我们写的所有Python代码在运行的时候都会被编译成code object.

何为编译,简单理解:
将需用到的py源代码/需执行的py文件 --(进行编译)-- 字节码 --(通过python的PVM虚拟机逐行解释)

我们来看看下面这个例子:

def f():
    pass


if __name__ == '__main__':
    # <code object f at 0x10b08cbe0, file "/Users/dengchuan/Desktop/pythonProject/default_param.py", line 1>
    print(f.__code__)
1
2
3
4
5
6
7

在装饰器那我们提到过:
当我们在python中所谓定义一个函数的时候,我们只是新建了一个变量,
然后这个变量里面保存了一个函数对象,也就是 function object !

也就是说, 该程序中 我们 define(定义) 的空函数f, 它本身就是一个 function object.
每一个function object 都会有它对应的一个 code object.
我们通过打印 print(f.__code__) , 就可以拿到 f这个function object里的code object.


# Code object里有什么

Q: 好, 那么, 这个code object中保存了什么呢?
我们可以看一下inspect模块的官方文档, 里面介绍了code object里有的attribute.
https://docs.python.org/zh-cn/3.10/library/inspect.html

image-20240622144451924

当然, 除了文档以外, 你也可以在程序中, 通过 dir(f.__code__) 也能拿到.

['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__','__lt__', '__ne__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_posonlyargcount', 'co_stacksize', 'co_varnames', 'replace']
1
2
3

可以看到 attribute 的部分是一样的, 我们将这些attribute分分类. 分别讨论下它们都是干什么的.

# co_code

首先是code object里的 co_code 这个 attribute.

属性 作用
co_code 保存着这一段代码 真正的, 用 binary(二进制) 表示的 byte code(字节码).
def f():
    pass


if __name__ == '__main__':
    code = f.__code__
    print(code.co_code)  # b'd\x00S\x00'
1
2
3
4
5
6
7

这个attribute我们一般不会去直接接触, 通常我们会通过dis模块去查看它对应的 我们人类认识的byte code(字节码).

image-20240622151324707

注: 现阶段, 不必纠结 这些字节码代表什么意思. 真的想了解的话, 去dis模块的官方文档查看.
只是想说, 当我们这段程序的运行超出了我们的认知的话, 可以看字节码的运行过程进行剖析. 这是一种解决途径.

# metadata相关

接下来的三个属性, 跟metadata相关. 也就是说, 它们跟真正的代码运行关系并不大, 它们是作为辅助数据出现的.

属性 作用
co_name 这段code的名字,一般就是你定义函数的名字.
co_filename 这段code是在哪个文件里被定义的.
co_lnotab 它保存的是一个mapping(映射),是一一对应的.
它将每一个byte code(字节码) 对应的cpython源代码 的行数 以二进制的形式进行了保存.
 def f():
    pass


if __name__ == '__main__':
    code = f.__code__

    print(code.co_name)  # f
    print(code.co_filename)  # /Users/dengchuan/Desktop/pythonProject/demo.py
    print(code.co_lnotab)  # b'\x00\x01'
1
2
3
4
5
6
7
8
9
10
# 运行时需要的

接下来, co_flags 和 co_stacksize 都是python 在 run time(运行时), Python virtual machine(虚拟机) 需要的一些数据.

属性 作用
co_flags 它是一个 bit map(位图)
co_stacksize 表明这段代码需要的栈的空间是多大

co_flags 简单来说, 就是在编译的时候, 会去判断下这个code有没有什么特别的属性.
比如有无 *args 、**kwargs , 是不是一个generator(生成器)、coroutine(协程) 等.
通过这些flags呢, python在运行这些代码的时候, 就可能有不同的 behavior(行为)..
比如, python在运行一个生成器函数和普通函数的时候,肯定是不一样的.

def f():
    pass


if __name__ == '__main__':
    code = f.__code__

    print(code.co_flags)  # 67
    print(code.co_stacksize)  # 1
1
2
3
4
5
6
7
8
9
# 关于输入参数的

接下来这部分就比较重要了, 是关于 输入参数的.
这些属性决定了 这个python在往里面传入数据的时候, 怎么处理这些参数的. 它们也是python函数进行重载的一个基础.

属性 作用
co_argcount 参数的个数 但参数不包括 *args **kwargs keyword only argument
co_posonlyargcount positional only argument 的个数.
/ 斜杠前面的参数都是 positional only argument
co_kwonlyargcount keyword only argument 的个数
* 星号后面的参数都是 keyword only argument

那么我们理解的关键在于, 什么是 positional only 什么是 keyword only ?!

先回顾一点知识:

def f(a, b):
    return a + b
1
2

针对上面程序的形参a和b, 你爱怎么传就怎么传, "但得保证 位置实参在关键字实参前面."

f(1, 2)
f(a=1, b=2)
f(1, b=2)
# f(a=1, 2) 报错: SyntaxError: positional argument follows keyword argument
1
2
3
4

再来看一段程序, 我们大部分人在写python程序的时候, argument(参数) 无非就写成这样.

def f(a, b=3, *args, **kwargs):
    pass


if __name__ == '__main__':
    code = f.__code__

    print(code.co_argcount)
    print(code.co_posonlyargcount)
    print(code.co_kwonlyargcount)
1
2
3
4
5
6
7
8
9
10

上述程序中的f函数的参数 a, b=3, *args, **kwargs , 既不是 positional only 也不是 keyword only !!
不信的话, 我们可以跑一下上面的程序瞅瞅.

2
0
0
1
2
3

那 positional only argument 到底是什么呢? 我们来看看下面这段代码.

def f(a, b=3, /, *args, **kwargs):
    print(a + b)


if __name__ == '__main__':
    code = f.__code__

    print(code.co_argcount)  # 2
    print(code.co_posonlyargcount)  # 2
    print(code.co_kwonlyargcount)  # 0
1
2
3
4
5
6
7
8
9
10

我们在 参数里加入了斜杠 / , 它表明 斜杠 "前面" 的参数 必须都是通过 positional(位置) 传参的形式传递进来.
也就是说 斜杠前面的参数都是 positional only 的!

我们对上述程序, 进行测试

f(3, 2)  # 5
f(3)  # 6
# ★ f(3, b=1) ※虽然这样不报错,但你会发现最后结果是6  b=1是不生效的!哈哈哈哈 要特别注意哦~
# f(a=3) 报错: TypeError: f() missing 1 required positional argument:'a'`(缺少一个必要的位置参数a)
1
2
3
4

知道了 positional only, 那 keyword only 是什么呢? 我们再来看看下面这段代码.

def f(a, *, b=3, **kwargs):
    print(a + b)


if __name__ == '__main__':
    code = f.__code__

    print(code.co_argcount)  # 1
    print(code.co_posonlyargcount)  # 0 
    print(code.co_kwonlyargcount)  # 1
1
2
3
4
5
6
7
8
9
10

我们在 参数里加入了星号 * , 它表明 星号 "后面" 的变量 必须都是通过 keyword(关键字) 传参的形式传递进来.
也就是说 星号后面的参数都是 keyword only 的!

Ps: * 前面的, 爱怎么传就怎么传, 不管. 只要保证 位置实参在关键字实参前面 就行.

f(a=2)
f(2)
f(2, b=4)
# f(2, 4) 报错: TypeError: f() takes 1 positional argument but 2 were given
1
2
3
4

接下来, 到了最复杂, 最容易混淆的, 世界上没有几个人能明白的, 这一大串的name啦!(´・Д・)」

先思考一个东西, byte code 应该如何设计?

后面的我就听不懂了.. 暂略. ╮( ̄▽ ̄"")╭


装饰器
单一职责原则

← 装饰器 单一职责原则→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式