python中函数调用的实质原理:

  python解释器(即python.exe)其实是用C语言编写的, 在执行python代码时,实际上是在用一个叫做Pyeval_EvalFramEx(C语言的函数)去执行代码中的函数,(实际上python中的程序实际上是运行在C语言之上的),运行此函数的时候,首先会在内存的堆区创建一个栈帧(stack frame),python中一切皆对象,在栈帧中间将要执行的代码编译成为字节码对象。 然后在栈帧的上下文中去运行字节码,可以用dis.dis()函数查看函数的字节码。

  

import dis
def foo():
bar()
def bar():
pass
print(dis.dis(foo)) 结果:
20 0 LOAD_GLOBAL 0 (bar)
2 CALL_FUNCTION 0
4 POP_TOP
6 LOAD_CONST 0 (None)
8 RETURN_VALUE
None

  

  字节码解释:当foo调用子函数bar时候,又会创建一个栈帧,然后将函数的控制权交给新的栈帧,然后去运行bar的字节码,然后就有了两个栈帧了。因为所有的栈帧都是分配在堆的内存上,(函数调用完毕不会被立即回收)这就决定了栈帧可以独立于调用者存在,(即foo即使不存在了退出了也没有关系,只要有指针指向bar的栈帧,就可以对其进行控制)具体看一下代码:foo()调用完毕了之后,由于全局变量指向了bar中的栈帧对象,所以print(frame.f_code.co_name)语句输出产生当前栈帧对象的对象名,即bar,然后caller_frame = frame.f_back语句将调用者(foo)的栈帧对象获取到,然后打印出来,即foo。

import inspect
import dis frame = None
def foo():
bar()
def bar():
global frame
frame = inspect.currentframe()
foo()
print(frame.f_code.co_name)
caller_frame = frame.f_back
print(caller_frame) 输出结果:
bar
foo

图解如下:heap(堆区), recurse(递归,图中意思即foo递归调用了bar)

堆区的PyFrameObject表示生成的栈帧对象,f_code表示执行函数的字节码,f_back表示调用者函数的字节码。

生成器的实现原理:


def gen_fun():
yield 'a'
name = 'bobby1'
yield 'b'
age = 30
return 'frank'
import dis
gen = gen_fun()
print(dis.dis(gen)) #打印gen函数的字节码
print(gen.gi_frame.f_lasti)
print(gen.gi_frame.f_locals)
next(gen)
print(gen.gi_frame.f_lasti)
print(gen.gi_frame.f_locals)
next(gen)
print(gen.gi_frame.f_lasti)
print(gen.gi_frame.f_locals) 输出结果:
20 0 LOAD_CONST 1 ('a')
2 YIELD_VALUE
4 POP_TOP 21 6 LOAD_CONST 2 ('bobby1')
8 STORE_FAST 0 (name) 22 10 LOAD_CONST 3 ('b')
12 YIELD_VALUE
14 POP_TOP 23 16 LOAD_CONST 4 (30)
18 STORE_FAST 1 (age) 24 20 LOAD_CONST 5 ('frank')
22 RETURN_VALUE
None
-1
{}
2
{}
12
{'name': 'bobby1'}

真是因为有yield实现生成器函数,使得我们可以自由控制函数的运行于暂停,这个是协程实现的基础。

生成器的应用实例:用生成器函数读取一行的超大文件。

def yield_str(file, spilt):
buf = ''
while True: while spilt in buf:
pos = buf.index(spilt)
yield buf[:pos]
buf = buf[pos + len(spilt):]
chunk = file.read(100)
if not chunk:
yield buf
break
buf += chunk with open('txt.txt', encoding='utf-8') as file:
for i in yield_str(file, ','):
print(i)

Python是如何实现生成器的原理的更多相关文章

  1. Python 中生成器的原理

    生成器的使用 在 Python 中,如果一个函数定义的内部使用了 yield 关键字,那么在执行函数的时候返回的是一个生成器,而不是常规函数的返回值. 我们先来看一个常规函数的定义,下面的函数 f() ...

  2. Python下探究随机数的产生原理和算法

    资源下载 #本文PDF版下载 Python下探究随机数的产生原理和算法(或者单击我博客园右上角的github小标,找到lab102的W7目录下即可) #本文代码下载 几种随机数算法集合(和下文出现过的 ...

  3. Python学习之旅—生成器对象的send方法详解

    前言 在上一篇博客中,笔者带大家一起探讨了生成器与迭代器的本质原理和使用,本次博客将重点聚焦于生成器对象的send方法. 一.send方法详解  我们知道生成器对象本质上是一个迭代器.但是它比迭代器对 ...

  4. Python源代码剖析笔记3-Python运行原理初探

    Python源代码剖析笔记3-Python执行原理初探 本文简书地址:http://www.jianshu.com/p/03af86845c95 之前写了几篇源代码剖析笔记,然而慢慢觉得没有从一个宏观 ...

  5. 小白的Python之路 day4 生成器并行运算

    一.概述 我们已经明白生成器内部的结构,其实就是通过像函数这样的东西实现的! 多线程和单线程:简单来说多线程就是并行运算,单线程就是串行运算 二.生成器执行原理 第一步:生成一个生成器  第二步:执行 ...

  6. python基础—迭代器、生成器

    python基础-迭代器.生成器 1 迭代器定义 迭代的意思是重复做一些事很多次,就像在循环中做的那样. 只要该对象可以实现__iter__方法,就可以进行迭代. 迭代对象调用__iter__方法会返 ...

  7. Python之迭代器,生成器

    迭代器 1.什么是可迭代对象 字符串.列表.元组.字典.集合都可以被for循环,说明他们都是可迭代的. from collections import Iterable l = [1,2,3,4] t ...

  8. python之迭代器与生成器

    python之迭代器与生成器 可迭代 假如现在有一个列表,有一个int类型的12345.我们循环输出. list=[1,2,3,4,5] for i in list: print(i) for i i ...

  9. day13 python学习 迭代器,生成器

    1.可迭代:当我们打印 print(dir([1,2]))   在出现的结果中可以看到包含 '__iter__', 这个方法,#次协议叫做可迭代协议 包含'__iter__'方法的函数就是可迭代函数 ...

随机推荐

  1. Odd-e CSD Course Day 2

    首先在第二天中其實談的更多的是在於 Test-Driven 的部分,而第一天談的偏向如何寫出一個好的 A-TDD 案例 但在第二天開始,就不太會照固定的 Topic 進行講述,而且讓團隊成員就像一個真 ...

  2. 从零开始学安全(三十七)●VM汇编环境搭建

    需要下载 vm 虚拟机 破解版连接 链接:https://pan.baidu.com/s/1r9MyPkNBmiYhQ8bdUxPmvQ 提取码:2o98 镜像文件和开发环境 链接:https://p ...

  3. ElasticSearch入门点滴

    这是Elasticsearch-6.2.4 版本系列的第一篇: ElasticSearch入门 第一篇:Windows下安装ElasticSearch ElasticSearch入门 第二篇:集群配置 ...

  4. Linux设置Swap虚拟内存方法

    linux可以文件或者分区来当作虚拟内存. 首先查看当前的内存和swap 空间大小(默认单位为k, -m 单位为M): free -m 查看swap信息,包括文件和分区的详细信息 swapon -s或 ...

  5. laravel5集成支付宝alipay扫码支付流程(Laravel 支付解决方案)

    首先我们来探讨如何在Laravel应用中使用支付宝进行支付,对此,GitHub上有很多相关的包,其中最流行的两个包:Omnipay For Laravel 5 & Lumen 和 Larave ...

  6. 通过Web API调用Action时各种类型输入参数传递值的方法

    本人微信公众号:微软动态CRM专家罗勇 ,回复280或者20180906可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . Dy ...

  7. 36.Odoo产品分析 (四) – 工具板块(6) – 午餐管理(2)

    查看Odoo产品分析系列--目录 接上一篇Odoo产品分析 (四) – 工具板块(6) – 午餐管理(1) 4 查看订单 点击"之前的订单",可以看到刚才的订单信息:  点击右边的 ...

  8. Git:六、分支管理(指针操作)

    1.基本操作 1)创建分支 git branch <name> 2)切换分支 git checkout <name> 1)&2)创建并切换分支 git checkout ...

  9. java设计模式——适配器模式 Java源代码

    前言:适配器模式就是把一个类的接口变换成客户端所能接受的另一种接口,从而使两个接口不匹配而无法在一起工作的两个类能够在一起工作.通常被用在一个项目需要引用一些开源框架来一起工作时,这些框架的内部都有一 ...

  10. 从0开始的Python学习009参数

    默认参数 对于参数有时候我们希望他是可选的,前面介绍了函数柯里化,当然还有其他的解决方案.如果不想给某些参数提供值的话,就让这写参数使用默认值.在函数定义的时候给参数赋值使用(参数,参数=值..... ...