While learning about struct
and by just testing an example provided as one of the answers given for question Python base64 encoding a list, I get a Python 3 related error which I don't know how to handle.
Test code:
import struct
import base64
l = [1, 2, 3, 4, 5]
y = struct.pack('<{}i'.format(len(l)), *l)
z = base64.b64encode(y)
# ... whatever further processing ...
y = base64.b64decode(z)
l = list(struct.unpack('<{}i'.format(len(y)/4), y))
which gives:
l = list(struct.unpack('<{}i'.format(len(y)/4), y))
struct.error: bad char in struct format
What should be modified to be Python 3 compatible ?
This is merely for exercise & learning; running Python 3.7 on Linux or 3.10 on Windows.
CodePudding user response:
/
always returns a floating-point value in Python 3, where in Python 2 it returned an int
if both arguments were int
s. Use //
instead.
l = list(struct.unpack('<{}i'.format(len(y) // 4), y))
Since you are using Python 3.7, you can use f-strings instead to simplify this.
l = list(struct.unpack(f'<{len(y) // 4}i', y))
(To this day, I still wonder why something like '<*i'
isn't supported to just extract as many values as possible from the byte string argument.)
CodePudding user response:
You should change the "true division" operator /
to "floor division" (sometimes called "integer division") //
:
l = list(struct.unpack('<{}i'.format(len(y)//4), y))
This is because in Python 2, division /
between int
s give an integer back, with the division rounded down (in the case of a non-zero remainder). In Python 3, division /
between int
s results in a float
, even in the case of the remainder being zero.
The floor division //
does the same thing as /
in Python 2. Note however that //
is also available in Python 2, so the code still works in Python 2 after changing /
to //
.
If you're interested in the thought process behind this change, check out PEP 238.