专栏首页python3由 Python2 和 Python3

由 Python2 和 Python3

这几天在做一个功能实现的时候,需要把别人用 Python2.6 写好的脚步转成 Python3.4 实现,大部分地方转化都没啥问题,但是在 socket.inet_aton() 转化的过程中出了点问题,花费我不少的精力去解决,先做个记录备忘,同时给后续需要的人做个提醒。

首先说一下,我在解决这个问题前期的思路有点问题,所以请关注最后的总结。

需求目的:把一个 ip 地址使用 socket.inet_aton() 转化后和一个字符串组合,然后算出 MD5。

下面是 Python2.6 的实现代码:

#!python2
# -*- coding: utf-8 -*-

import socket
import hashlib

if __name__ == '__main__':
    ip = '192.168.1.12'
    base_str = 'testSTR'
    ip_md5 = hashlib.md5(socket.inet_aton(ip) + base_str).digest().encode('hex')
    print(ip_md5)

运行后的输出结果为:

fc138bb4748a18f885cc321c2c6396e2

如果原封不动的使用 Python3.4 运行后,报错如下:

Traceback (most recent call last):
  File "socket34.py", line 25, in <module>
    test1()
  File "socket34.py", line 10, in test1
    ip_md5 = hashlib.md5(socket.inet_aton(ip) + base_str).digest().encode('hex')
TypeError: can't concat bytes to str

提示说的是,socket.inet_aton(ip) 的返回值是 bytes 类型,所以不能和 str 类型的 base_str 直接进行连接操作。 也就是说 Python2.6 和 python3.4 中对于 socket.inet_aton(ip) 的实现是有差异的,查官方文档吧。 python2.6文档说明:

socket.inet_aton(ip_string) Convert an IPv4 address from dotted-quad string format (for example, ‘123.45.67.89’) to 32-bit packed binary format, as a bytes object four characters in length.

python3.4 文档说明:

socket.inet_aton(ip_string) Convert an IPv4 address from dotted-quad string format (for example, ‘123.45.67.89’) to 32-bit packed binary format, as a string four characters in length.

好吧,返回值类型不同,为了保证和原脚本逻辑一致,我就做个转化,把 bytes 主动转换为 str 类型再连接,修改后的代码如下:

#!python3
# -*- coding: utf-8 -*-

import socket
import hashlib

if __name__ == '__main__':
    ip = '192.168.1.12'
    base_str = 'testSTR'
    str_md5 = socket.inet_aton(ip).decode('gbk') + base_str
    ip_md5 = hashlib.md5(str_md5).digest().encode('hex')
    print(ip_md5)

代码通过 decode 把 bytes 使用 gbk 的方式解码成 str,至于为什么用 gbk,是因为我对比了下,只有 gbk 编码方式解码后的输出才和 python2.6 中的 str 返回值结果一致。

行,赶紧运行一把试试看。。。还是报错了,这次的报错内容变了:

Traceback (most recent call last):
  File "socket34.py", line 34, in <module>
    test1()
  File "socket34.py", line 12, in test1
    ip_md5 = hashlib.md5(str_md5).digest().encode('hex')
TypeError: Unicode-objects must be encoded before hashing

看起来 hashlib.md5() 在 Python2.6 和 Python3.4 中的实现也有差异,继续看文档。 python2.6文档说明:

You can now feed this object with arbitrary strings using the update() method.

python3.4 文档说明:

You can now feed this object with bytes-like objects (normally bytes) using the update() method.

依然是编码格式的问题,Python2.6 中参数传入的是 str,但是 Python3.4 中参数需要传入 bytes,那就继续转码吧。 再次转码后的代码如下:

#!python3
# -*- coding: utf-8 -*-

import socket
import hashlib

if __name__ == '__main__':
    ip = '192.168.1.12'
    base_str = 'testSTR'
    str_md5 = socket.inet_aton(ip).decode('gbk') + base_str
    ip_md5 = hashlib.md5(str_md5.encode('gbk')).digest().encode('hex')
    print(ip_md5)

运行后再次报错:

Traceback (most recent call last):
  File "socket34.py", line 33, in <module>
    test1()
  File "socket34.py", line 11, in test1
    ip_md5 = hashlib.md5(str_md5.encode('gbk')).digest().encode('hex')
AttributeError: 'bytes' object has no attribute 'encode'

好吧,继续看文档。 python2.6文档说明:

hash.digest() Return the digest of the strings passed to the update() method so far. This is a string of digest_size bytes which may contain non-ASCII characters, including null bytes.

python3.4 文档说明:

hash.digest() Return the digest of the data passed to the update() method so far. This is a bytes object of size digest_size which may contain bytes in the whole range from 0 to 255.

这次更严重,encode() 直接用不了,换方法吧,更新后的代码如下:

#!python3
# -*- coding: utf-8 -*-

import socket
import hashlib
import binascii

if __name__ == '__main__':
    ip = '192.168.1.12'
    base_str = 'testSTR'
    str_md5 = socket.inet_aton(ip).decode('gbk') + base_str
    ip_md5 = binascii.hexlify(hashlib.md5(str_md5.encode('gbk')).digest()).decode()
    print(ip_md5)

运行后的输出结果:

fc138bb4748a18f885cc321c2c6396e2

终于得到了最终结果,激动,不过再回头一看,如果知道这几个函数的使用方式的话,就不需要 decode() 然后又 encode(),比如稍微优化后的代码如下:

#!python3
# -*- coding: utf-8 -*-

import socket
import hashlib
import binascii

if __name__ == '__main__':
    ip = '192.168.1.12'
    base_str = 'testSTR'
    str_md5 = socket.inet_aton(ip) + base_str.encode()
    ip_md5 = binascii.hexlify(hashlib.md5(str_md5).digest()).decode()
    print(ip_md5)

总结:

  1. Python3 新增了 bytes 类型,对于 bytes 的转换逻辑要特别清楚,这地方涉及了编码类型,要特别关注;
  2. 在使用一些函数前,一定要搞清楚这个函数的具体实现,必须清楚的知道使用了这个函数是什么效果,而不仅仅是看到暂时的效果,或者经验主义的去调用(上面例子的最后一步,其实我一开始不是用的 binascii,而是用的现成的 md5 转换函数,导致 encode() 成了 utf-8 格式,而浪费了不少时间去定位);
  3. 解决问题过程中,思路一定要清晰,不能靠猜,越猜越错;
  4. 先弄明白问题的根本原因,直接从根源上去解决,比一步步的就错解错,效果更好。

本文原创发布于公众号「sylan215」,十年测试老兵的原创干货,关注我,涨姿势!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ReadDocs

    Sphinx 是一个基于 Python 的文档生成工具,最早只是用来生成 Python 官方文档,随着工具的完善,越来越多的知名的项目也用他来生成文档,甚至完全...

    故事尾音
  • Hybris Storefront里产品图片显示不出来的分析方法

    在Hybris Backoffice里新建一个product,维护了一些图片信息,然而在Storefront上进行搜索时,无论结果列表,还是产品明细页面里,都没...

    Jerry Wang
  • TensorFlow 内核剖析

    这是我找的一个Tensorflow的书,作者是刘光聪。书写的非常不错,我也借此机会学习一波。书中的TensorFlow使用的是1.2版本,目前来说算是很新的。 ...

    故事尾音
  • 在python脚本中执行shell命令的方法

    最近在写python的一些脚本,之前使用python都是在django中使用,可能大部分内容都是偏向于后端开发方面的,最近在写一些脚本的时候,发现了pyt...

    AsiaYe
  • python处理字符串的一些技巧

    最近两个周,在MySQL方向的投入比较少,都是在看一些前人写的python脚本,之前使用python都是在django中写后端逻辑,对于python的脚本...

    AsiaYe
  • 2019 年 11 月编程语言排行榜

    TIOBE 11 月份的编程语言排行榜已经公布,官方的标题是: C 语言已经很接近 Java 了,Swift 排名进入了前 10,Rust 排名达到历史新高。

    良月柒
  • Python高级语法

    本篇博客记录的是一些python的高级用法,更加深刻的理解Python的语法,。

    故事尾音
  • python和shell常用基本命令的对应关系

    平时工作的过程中,我个人习惯用python写后端的逻辑,然后使用shell写一些Linux环境下面的脚本,对于python脚本的使用,其实还是比较少的,最...

    AsiaYe
  • CIFAR10数据集实战-LeNet5神经网络(中)

    用户6719124
  • 在Pypi上发表自己的Python库

    原则上安装了pip的环境都有setuptools,但并不影响你去尝试升级一下它。 pip install --upgrade setuptools

    故事尾音

扫码关注云+社区

领取腾讯云代金券