我的编程空间,编程开发者的网络收藏夹
学习永远不晚

gevent 迁移 Python 3

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

gevent 迁移 Python 3

时隔一年多,gevent 的作者 Denis Bilenko 终于从创业的百忙之中,抽出时间打算 review 我在 2012 年的时候完成的 gevent 到 Python 3 的迁移工作。

Skype 交谈中,Denis 问了几个问题,我发现有不少改动我已经忘记了当初写的原因了,这个案例教育我们,在做较大的修改的时候,尽量拆分成多个较小的提交,每个提交消息都尽量写清楚。^_^

因为过了一年多,gevent 的 master 上也改动了一些。我尝试了做 merge,发现结果不是很理想,再加上对当时修改又不是很满意了,于是乎,我选择了参考原来的改动,重新迁移一次。

插叙一段小插曲。其实在 Denis 联系我之前,我已经放弃他了——因为他实在是很久很久没有在 gevent 上活跃开发了,gevent 1.0 感觉也是憋了好久憋出来的。当时连蟒爹的 Tulip/asyncio 都眼瞅着要发布了,我就直接 fork 了个项目叫 gevent3,也就是 Python 3 版的、基于 asyncio 的 gevent,这个 gevent3 有机会再跟大家介绍。没想到刚 fork 完没做多久,就发生了故事开头写的事情。

言归正传。接下来我分段介绍我这几个月用业余时间几乎做完的第二次迁移工作,希望能对也在做向 Python 3 迁移工作的同学们有点帮助。

Denis 对迁移工作的要求是,用同一套代码,同时支持 Python 2.6, 2.7 和 3.3。除了 greenlet,最好不要再引入其他的依赖,甚至是 six——一个专注于解决用同一套代码支持不同 Python 版本问题的库。

软柿子

老虎吃天,无从下口。面对庞大的代码量,还得先捡软柿子捏。

比如说,Python 3 用 int 替代了 Python 2 的 long(和 int)。six 对这种情况有这么一段定义:

if PY3:
    integer_types = int,
else:
    integer_types = (int, long)

那么就可以简单地把所有能换成 integer_types 的地方都换成 integer_types,就像这样:

         def __init__(self, fileno, mode=None, close=True):
-            if not isinstance(fileno, (int, long)):
+            if not isinstance(fileno, integer_types):
                 raise TypeError('fileno must be int: %r' % fileno)

类似的软柿子还有:

if PY3:
    string_types = str,
    integer_types = int,
    text_type = str
    xrange = range
else:
    string_types = basestring,
    integer_types = (int, long)
    text_type = unicode
    xrange = xrange

这些替换都是很简单的,虽然只是一个开始,但是可以让接下来更复杂的工作有一个好的开始。请参考:https://pythonhosted.org/six/#constants

乾坤大挪移

Python 3 中,很多模块都改了名字,幸好多半接口并没有变化,所以为了同时能够支持 Python 2 和 3,可以简单地这么搞:

-from Queue import Full, Empty
+try:
+    from Queue import Full, Empty
+except ImportError:
+    from queue import Full, Empty

或者这样搞:

-import urllib2
+try:
+    import urllib2
+except ImportError:
+    from urllib import request as urllib2

还有一些其他不少重命名和重新规划,请参见:http://python3porting.com/stdlib.html

将来时

在 Python 3 中,print 变成了一个函数,这直接意味着这样的代码是语法错误的:

print "Hello, world!"

为了实现同一份代码同时支持 Python 2 和 3,这里我们可以用到一个叫做 __future__import——这个 import 可以在某些老版本的 Python 中添加一些新版本才有的语言特性。对于 print 来说,Python 3 风格的 print() 函数自 Python 2.6 起开始出现在 __future__ 中。谢天谢地,gevent 及时摒弃了 Python 2.5 的支持,我们可以统一使用 Python 3 风格的 print() 来写所有代码,而做到这一点只需要在所有用到 print 的 Python 文件开头写这么一句:

from __future__ import print_function

这样一来,这些文件就可以使用 Python 3 风格的 print() 函数了。最抓人的是,如果以后打算放弃 Python 2 支持的话,只需要(甚至不需要)把这一行 import 语句删掉就可以了。

要注意的是,from __future__ import ... 必须出现在所有非注释类代码的前面。

更多细节可以参考这里:http://docs.python.org/3/library/__future__.html

ps: 还有个小插曲。gevent 的代码里从 Python 代码树拷贝了一些测试文件,比如 greentests/2.6/test__xxxxxx.py,用以测试 monkey patch 上去的 gevent 代码的正确性。这些测试只会在指定 Python 版本下才会执行,所以我就没有给 2.6 和 2.7 的代码加 print_function。奇怪的事情发生了!2.6 和 2.7 的某个测试居然开始抱怨说,print "Hello, world!" 语法错误!没查原因我就默默地把 2.6 和 2.7 的测试文件都加上了 print_function……结果咯,Denis 不愿意,还是得去查原因。最后发现 greentest/monkey_test.py 那货是亲自 exec() 的 2.6 和 2.7 下面的某些测试代码,而我给 monkey_test.py 也加上了 print_function……所以说,有 exec() 调用存在的情况下,不要轻易相信 from __future__ import xxxx 只对当前文件起作用

异常处理

这是轻敌了的一部分。

一开始只是以为 Python 2 与 3 之间,异常处理的区别只在于语法——对于 Python 2.6 及以上版本只要这样改就好了:

 try:
     1/0
-except Exception, ex:
+except Exception as ex:
     pass

原来这里有大买卖。

同一段代码,最后加多一句:

try:
    1/0
except Exception as ex:
    pass
print(ex)

在 Python 2 上是这样的结果:

$ python2.7 extest.py 
integer division or modulo by zero

在 Python 3 上却是:

$ python3.3 extest.py 
Traceback (most recent call last):
  File "extest.py", line 5, in <module>
    print(e)
NameError: name 'ex' is not defined

原来,Python 3 去掉了 sys.exc_clear() 函数,把该行为嵌入了语言内部——也就是说,只要是出了 except 子句,Python 3 的解释器会自动清除异常状态,还会捎带手把异常变量引用(as 出来的那个)删掉。举个例子,还是同一段代码,稍微改一下:

import sys
try:
    1/0
except Exception as ex:
    pass
print(sys.exc_info())

在 Python 2 中执行:

$ python2.7 exclear.py 
(<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x104d1a0e0>)

但在 Python 3 中:

$ python3.3 exclear.py 
(None, None, None)

原来如此!基于这些知识,gevent 的某些代码就得改了——原先在 except 子句中经常有 exc_clear() 之后又做了一些事情,现在就得改成在 except 子句外面来做这些事情。比如 socket.recv() 就得这么改(片段):

     def recv(self, *args):
         while True:
             try:
                 return sock.recv(*args)
             except error as ex:
                 if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
                     raise
-                sys.exc_clear()
-                self._wait(self._read_event)
+                if not PY3:
+                    sys.exc_clear()
+            self._wait(self._read_event)

我还是挺喜欢 Python 3 的这个改变的,因为这样一来异常处理就非常干净整洁了,except 子句画地为牢,有效地限制了无用信息的外漏;另外这种限制还可以在一定程度上建议人们,不要在 except 子句里面写太多的业务逻辑,把异常处理好,有啥事儿咱出来再说。

另外,Python 3 还在异常的栈跟踪信息上做了一些改进,比如这么一段代码:

try:
    1/0
except Exception as ex:
    None.non_exist()

就是在处理异常的时候,又弄坏了别的东西。Python 2 执行是这样的:

$ python2.7 tb.py 
Traceback (most recent call last):
  File "tb.py", line 4, in <module>
    None.non_exist()

Python 3:

$ python3.3 tb.py 
Traceback (most recent call last):
  File "tb.py", line 2, in <module>
    1/0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tb.py", line 4, in <module>
    None.non_exist()
AttributeError: 'NoneType' object has no attribute 'non_exist'

高端、大气、上档次有木有!Python 3 是这么实现这种异常链的:

  1. 当第一个异常对象产生时,traceback 信息会保存在该对象的 __traceback__ 属性中;
  2. 当第二个异常对象产生时,因为是在第一个异常的 except 子句中,所以第一个异常对象被保存在了第二个异常对象的 __context__ 属性中(当然第二个异常的 __traceback__ 属性同样保存了第二个异常的栈跟踪信息);
  3. 依次这样链下去,你就会得到一个异常链,你可以通过访问比如 ex.__context__.__context__.__traceback__ 来找到爷爷异常的栈跟踪信息。

这个美好的功能在这次 gevent 的迁移最后引来了好大一个麻烦,等讲到时再细说。

(未完待续,附项目地址:https://github.com/fantix/gevent)

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

gevent 迁移 Python 3

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

gevent 迁移 Python 3

时隔一年多,gevent 的作者 Denis Bilenko 终于从创业的百忙之中,抽出时间打算 review 我在 2012 年的时候完成的 gevent 到 Python 3 的迁移工作。Skype 交谈中,Denis 问了几个问题,我
2023-01-31

3、Jenkins升级和迁移

一、Jenkins升级Jenkins的开发迭代非常快,每周发布一个开发版本,长期支持版每半年更新一次(ps:大版本更新)。如此频繁的更新,怎么升级呢?war:下载新版的war文件,替换旧版本war文件。重启即可。二进制:卸载旧版本,安装新版
2023-01-31

Python是否会迁移到GitHub

这期内容当中小编将会给大家带来有关Python是否会迁移到GitHub,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Python 软件基金会使用的源码库版本控制系统经历了多次变迁,从 CVS 到 Subv
2023-06-17

python 动态迁移solr数据

上项目的时候,遇见一次需求,需要把在线的 其中一个 collection 里面的数据迁移到另外一个collection下,于是就百度了看到好多文章,其中大部分都是使用导入的方法,没有找到在线数据的迁移方法。于是写了python脚本,分享出来
2023-01-31

利用python 迁移单个redis的

环境的是python3 / pip3import sysimport redis# 迁移hashdef moveHash(cursor): cursor, data = r.hscan(key, cursor) for each
2023-01-31

怎么把应用从Eclipse 3.x 迁移到IntelliJ IDEA

本篇内容主要讲解“怎么把应用从Eclipse 3.x 迁移到IntelliJ IDEA”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么把应用从Eclipse 3.x 迁移到IntelliJ I
2023-06-17

Python虚拟环境迁移的实现

本文主要介绍了Python虚拟环境迁移的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-03-06

Python虚拟环境迁移如何实现

本篇内容介绍了“Python虚拟环境迁移如何实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!使用python开发脚本使用的时候难免会遇到需
2023-07-05

利用Python实现图片风格迁移

本篇内容主要讲解“利用Python实现图片风格迁移”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“利用Python实现图片风格迁移”吧!1. 什么是图片的风格迁移?所谓图片风格迁移,是指利用程序算
2023-06-02

讲解正确进行下一代网络迁移的3个方法!

     目前,还有些小伙伴不会“正确进行下一代网络迁移”,今天编程学习网就是来跟大家一起探讨的,文中3个方法,有需要的小伙伴,可以参考一下。必须要认真阅读的哦!人们说,在旅行中,到达目的地只是旅程乐趣的一部分。但是在网络迁移中,迁移完成是整个过程的关键目的。  下一代
讲解正确进行下一代网络迁移的3个方法!
2024-04-18

Python虚拟环境迁移的方法是什么

在Python中,可以使用pip工具来导出虚拟环境的依赖项列表,并在新环境中重新安装这些依赖项。以下是迁移Python虚拟环境的一般步骤:在原始环境中导出依赖项列表:pip freeze > requirements.txt在新环境中创建虚
Python虚拟环境迁移的方法是什么
2024-04-09

Python虚拟环境迁移的方法是什么

Python虚拟环境迁移迁移Python虚拟环境涉及将现有虚拟环境及其依赖项从一台计算机转移到另一台计算机。此过程可用于在开发和生产环境之间移动、进行测试或创建备份。迁移步骤包括导出虚拟环境、复制导出的目录、创建新虚拟环境、导入导出目录并重新激活环境。其他方法包括使用pipfreeze/pipinstall或venvpack打包和解包环境。
Python虚拟环境迁移的方法是什么
2024-04-13

使用2to3将代码移植到Python 3

概述#几乎所有的Python 2程序都需要一些修改才能正常地运行在Python 3的环境下。为了简化这个转换过程,Python 3自带了一个叫做2to3的实用脚本(Utility Script),这个脚本会将你的Python 2程序源文件作
2023-01-31

Python中MySQL数据迁移到MongoDB脚本的方法

MongoDB简介 MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。 MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富
2022-06-04

Python 虚拟环境迁移到其他电脑的实现

本文主要介绍了Python 虚拟环境迁移到其他电脑的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-15

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录