Home > front end >  Porting apply() from Python2 to Python3
Porting apply() from Python2 to Python3

Time:09-17

I'm migrating some legacy python2 code to python3, and came across this function:

FRAMESTART_A = 231
FRAMETYPE_VIDEO = 6

def prepMessage(self, x):
        self.nextMessage = []
        bytes = []
        bytes.append(FRAMESTART_A)  # frame start
        bytes.append(FRAMETYPE_VIDEO)  # addresss
        for n in range(self.width):
            # set values from [0, width]
            value = 65535 * \
                (1.0 - (math.pow(0.5 * (math.sin(x / 2000   time.time())   1), 4)))
            bytes.append(value)
        for n in range(50 - self.width):
            # set bytes from [width, :] to zero
            bytes.append(0)
        message = apply(struct.pack,["BB" "H"*(len(bytes)-2)] bytes)
        self.nextMessage.append(message)

Conceptually, I get that this is creating a byte array of items that's always 50 bytes long, with the last bytes padded to zero. In Python3, apply() doesn't exist anymore, so the problem is with the line:

    message = apply(struct.pack,["BB" "H"*(len(bytes)-2)] bytes)

Reading the docs, it looks like I can just use the * operator to apply the function to a list of arguments, as so:

message = struct.pack(*(["BB"   "H" * (len(bytes) - 2)]   bytes))

But this produces a runtime error:

    message = struct.pack(*(["BB"   "H" * (len(bytes) - 2)]   bytes))
    struct.error: required argument is not an integer

Looking at the python3 struct.pack documentation, it looks like I should get input a formatting string that looks like "BBHHHHHHH..." and then give it the byte array. However, the original code works! So I'm not sure how message = apply(struct.pack,["BB" "H"*(len(bytes)-2)] bytes) worked, unless struct.pack worked substantially different in python2.

What's the right way to port this forward to get working code?

CodePudding user response:

If you want to recreate apply in Python 3, it's pretty easy:

def apply(func, args, kwargs=None):
    return func(*args) if kwargs is None else func(*args, **kwargs)

If you want to make the code work on either Py2 or Py3, just put this at the top of your file to make a fallback apply when it's not provided:

try:
    apply
except NameError:
    def apply(func, args, kwargs=None):
        return func(*args) if kwargs is None else func(*args, **kwargs)

That said, you don't really need apply either on Py2 or Py3; any time you see:

apply(somefunc, someargs)

like this, you can change it to just:

somefunc(*someargs)

so in this case, it would be:

message = struct.pack(*["BB" "H"*(len(bytes)-2)] bytes)

or even more simply (since concatenating one thing onto the front only to unpack immediately is unnecessary when you can just pass that one thing and unpack the rest without the extra temporary):

message = struct.pack("BB" "H"*(len(bytes)-2), *bytes)
  • Related