由 Python2 和 Python3 中 socket.inet_aton() 实现不同引发的血案

这几天在做一个功能实现的时候,需要把别人用 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. 先弄明白问题的根本原因,直接从根源上去解决,比一步步的就错解错,效果更好。

原文地址:http://blog.51cto.com/sylan215/2141208

时间: 2024-10-30 00:59:33

由 Python2 和 Python3 中 socket.inet_aton() 实现不同引发的血案的相关文章

有关python2与python3中关于除的不同

有关python2与python3中关于除的不同 python中2版本与3版本关于除的处理还是有一些差异的. 在python 2.7.15中除(/)是向下取整的,即去尾法. 123/10 # 结果 12 128/10 # 结果 12 在python 3中除(/)是相对于2版本精确了一些,例如: print(123/10) # 结果 12.3 原文地址:https://www.cnblogs.com/ZN-225/p/10574748.html

python2和python3中的编码问题

开始拾起python,准备使用python3, 造轮子的过程中遇到了编码的问题,又看了一下python3和python2相比变化的部分. 首先说个概念: unicode:在本文中表示用4byte表示的unicode编码,也是python内部使用的字符串编码方式. utf-8:在本文中指最少1byte表示的unicode编码方式 我在使用 if isinstance(key,unicode): key= key.encode('utf-8') 的时候,发现key值被转成了b'foo',b'bar'

python3中socket套接字的编码问题解决

一.TCP 1.tcp服务器创建 #创建服务器 from socket import * from time import ctime #导入ctime HOST = '' #任意主机 PORT = 21567 #随机提供个端口号 BUFSIZ = 1024 # 缓冲区大小设置为1KB,可以根据网络性能和程序需要改变这个容量 ADDR = (HOST, PORT) tcpSerSock = socket(AF_INET, SOCK_STREAM) #分配了 TCP 服务器套接字 tcpSerSo

Python2和Python3中print的不同点

在Python2和Python3中都提供print()方法来打印信息,但两个版本间的print稍微有差异 主要体现在以下几个方面: 1.python3中print是一个内置函数,有多个参数,而python2中print是一个语法结构: 2.Python2打印时可以不加括号:print 'hello world', Python3则需要加括号   print("hello world") 3.Python2中,input要求输入的字符串必须要加引号,为了避免读取非字符串类型发生的一些行为

每日一读:《关于python2和python3中的range》

官网原话是这么说的:In many ways the object returned by range() behaves as if it is a list, but in fact it isn't. It is an object which returns the successive items of the desired sequence when you iterate over it, but it doesn't really make the list, thus sav

Python2 和 Python3 中默认编码的差异

最近在使用 Python3.4 做一些脚本实现,发现对于编码的处理上和 Python2.6 有很大的不同,就此机会把相关知识做个梳理,方便需要的时候查阅. 先说下概念和差异: 脚本字符编码:就是解释器解释脚本文件时使用的编码格式,可以通过 # -\*- coding: utf-8 -\*- 显式指定解释器字符编码:解释器内部逻辑过程中对 str 类型进行处理时使用的编码格式Python2 中默认把脚步文件使用 ASCII 来处理(历史原因请 Google)Python2 中字符串除了 str 还

python2 与python3中最大的区别(编码问题bytes&amp;str

1,在python2.x 中是不区分bytes和str类型的,在python3中bytes和str中是区分开的,str的所有操作bytes都支持 python2 中 >>> s = "abcdefg" >>> b = s.encode()    #或者使用下面的方式 >>> b = b"abcdefg">>> type(b)<type 'str'> python3中     #str

nose在python2与python3中的包的自动发现用例的区别

最近在使用python3,同样装了nose,发现自动发现用例总是有问题,如下面的代码结婚 testcase |------ __init__.py |------ test_bb.py test_bb.py中文件为: def test_qq(): pass Python3中: 再使用nose执行testcase提示: ----------------------------------------------------------------------Ran 0 tests in 0.001

python2和python3中的关键字的区别--keyword模块

一.python3.6中的: 共33个关键字: 通过导入keyword模块,查看python所有的关键字.在ipython中: Python 3.6.0 |Anaconda 4.3.1 (64-bit)| (default, Dec 23 2016, 11:57:41) [MSC v.1900 64 bit (AMD64)]Type "copyright", "credits" or "license" for more information.