python - 在代码中兼容地使用字符串和类字节对象以在 Python 2 和 3 中运行

标签 python python-3.x string python-2.7

我正在尝试修改下面显示的代码,它在 Python 2.7.x 中工作,因此它也可以在 Python 3.x 中保持不变。但是,我遇到了以下无法在第一个函数 bin_to_float() 中解决的问题,如下面的输出所示:

float_to_bin(0.000000): '0'
Traceback (most recent call last):
  File "binary-to-a-float-number.py", line 36, in <module>
    float = bin_to_float(binary)
  File "binary-to-a-float-number.py", line 9, in bin_to_float
    return struct.unpack('>d', bf)[0]
TypeError: a bytes-like object is required, not 'str'

我试图通过在调用 struct.unpack() 之前添加一个 bf = bytes(bf) 来解决这个问题,但这样做会产生它自己的 类型错误:

TypeError: string argument without an encoding

所以我的问题是是否可以解决此问题并实现我的目标?如果是这样,怎么办?最好以一种适用于两个版本的 Python 的方式。

这是在 Python 2 中工作的代码:

import struct

def bin_to_float(b):
    """ Convert binary string to a float. """
    bf = int_to_bytes(int(b, 2), 8)  # 8 bytes needed for IEEE 754 binary64
    return struct.unpack('>d', bf)[0]

def int_to_bytes(n, minlen=0):  # helper function
    """ Int/long to byte string. """
    nbits = n.bit_length() + (1 if n < 0 else 0)  # plus one for any sign bit
    nbytes = (nbits+7) // 8  # number of whole bytes
    bytes = []
    for _ in range(nbytes):
        bytes.append(chr(n & 0xff))
        n >>= 8
    if minlen > 0 and len(bytes) < minlen:  # zero pad?
        bytes.extend((minlen-len(bytes)) * '0')
    return ''.join(reversed(bytes))  # high bytes at beginning

# tests

def float_to_bin(f):
    """ Convert a float into a binary string. """
    ba = struct.pack('>d', f)
    ba = bytearray(ba)
    s = ''.join('{:08b}'.format(b) for b in ba)
    s = s.lstrip('0')  # strip leading zeros
    return s if s else '0'  # but leave at least one

for f in 0.0, 1.0, -14.0, 12.546, 3.141593:
    binary = float_to_bin(f)
    print('float_to_bin(%f): %r' % (f, binary))
    float = bin_to_float(binary)
    print('bin_to_float(%r): %f' % (binary, float))
    print('')

最佳答案

要使用从字面上使用两者之间不同数据类型的库来制作适用于 Python 2 和 3 中字节的可移植代码,您需要使用每个字符串的适当文字标记显式声明它们(或添加 from __future__ import unicode_literals到执行此操作的每个模块的顶部)。此步骤是为了确保您的代码内部的数据类型正确。

其次,决定继续支持 Python 3,并针对 Python 2 进行回退。这意味着用 unicode 覆盖 str,并找出方法/函数不要在两个 Python 版本中返回相同的类型,应该修改和替换以返回正确的类型(作为 Python 3 版本)。请注意 bytes 也是一个保留字,所以不要使用它。

将这些放在一起,您的代码将如下所示:

import struct
import sys

if sys.version_info < (3, 0):
    str = unicode
    chr = unichr


def bin_to_float(b):
    """ Convert binary string to a float. """
    bf = int_to_bytes(int(b, 2), 8)  # 8 bytes needed for IEEE 754 binary64
    return struct.unpack(b'>d', bf)[0]

def int_to_bytes(n, minlen=0):  # helper function
    """ Int/long to byte string. """
    nbits = n.bit_length() + (1 if n < 0 else 0)  # plus one for any sign bit
    nbytes = (nbits+7) // 8  # number of whole bytes
    ba = bytearray(b'')
    for _ in range(nbytes):
        ba.append(n & 0xff)
        n >>= 8
    if minlen > 0 and len(ba) < minlen:  # zero pad?
        ba.extend((minlen-len(ba)) * b'0')
    return u''.join(str(chr(b)) for b in reversed(ba)).encode('latin1')  # high bytes at beginning

# tests

def float_to_bin(f):
    """ Convert a float into a binary string. """
    ba = struct.pack(b'>d', f)
    ba = bytearray(ba)
    s = u''.join(u'{:08b}'.format(b) for b in ba)
    s = s.lstrip(u'0')  # strip leading zeros
    return (s if s else u'0').encode('latin1')  # but leave at least one

for f in 0.0, 1.0, -14.0, 12.546, 3.141593:
    binary = float_to_bin(f)
    print(u'float_to_bin(%f): %r' % (f, binary))
    float = bin_to_float(binary)
    print(u'bin_to_float(%r): %f' % (binary, float))
    print(u'')

我使用 latin1 编解码器只是因为这是最初定义的字节映射,而且它似乎可以工作

$ python2 foo.py 
float_to_bin(0.000000): '0'
bin_to_float('0'): 0.000000

float_to_bin(1.000000): '11111111110000000000000000000000000000000000000000000000000000'
bin_to_float('11111111110000000000000000000000000000000000000000000000000000'): 1.000000

float_to_bin(-14.000000): '1100000000101100000000000000000000000000000000000000000000000000'
bin_to_float('1100000000101100000000000000000000000000000000000000000000000000'): -14.000000

float_to_bin(12.546000): '100000000101001000101111000110101001111110111110011101101100100'
bin_to_float('100000000101001000101111000110101001111110111110011101101100100'): 12.546000

float_to_bin(3.141593): '100000000001001001000011111101110000010110000101011110101111111'
bin_to_float('100000000001001001000011111101110000010110000101011110101111111'): 3.141593

同样,但这次是在 Python 3.5 下)

$ python3 foo.py 
float_to_bin(0.000000): b'0'
bin_to_float(b'0'): 0.000000

float_to_bin(1.000000): b'11111111110000000000000000000000000000000000000000000000000000'
bin_to_float(b'11111111110000000000000000000000000000000000000000000000000000'): 1.000000

float_to_bin(-14.000000): b'1100000000101100000000000000000000000000000000000000000000000000'
bin_to_float(b'1100000000101100000000000000000000000000000000000000000000000000'): -14.000000

float_to_bin(12.546000): b'100000000101001000101111000110101001111110111110011101101100100'
bin_to_float(b'100000000101001000101111000110101001111110111110011101101100100'): 12.546000

float_to_bin(3.141593): b'100000000001001001000011111101110000010110000101011110101111111'
bin_to_float(b'100000000001001001000011111101110000010110000101011110101111111'): 3.141593

需要做更多的工作,但在 Python3 中,您可以更清楚地看到类型是按正确的字节完成的。我还将您的 bytes = [] 更改为 bytearray 以更清楚地表达您尝试做的事情。

关于python - 在代码中兼容地使用字符串和类字节对象以在 Python 2 和 3 中运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39252140/

相关文章:

python - 在 Flask 工厂设置中反射(reflect)不同的数据库

iphone - NSString 与 NSMutableString 与 stringByAppendingString

python - 如何使用 Django 功能而不是所有键来索引刚刚选择的 json 键?

python - 使用 numpy 数组计算相似元组的数量

python - 如何在 python 中执行 Linux 实用程序并回答其回复?

python - Python中重定向后获取页面的url

python - 为什么 y = [m*x + b for x in Months] 有效?

python - 获取嵌套括号中的所有文本 (Python)

c - argv 输出错误?

android - 如何使用 Eclipse ADT 在 Android 项目中查找字符串资源的声明