Peter Constable
Peter Constable

Reputation: 3570

Python: how to pass tuple elements from a tuple slice rather than tuple

I'm learning Python and haven't yet learned completely about how arg arrays are handled in function calls. That's what my question is about: I'm passing a slice from a tuple into a function expecting the function will see multiple arguments, but it's just seeing one. It's not clear to me if this is a normal pattern that I need to work with in the called function, or if I should be doing something differently.

In my project, I'm parsing a binary file format (OpenType font file). The format is a complex tree of many different struct types: some structs are comprised of basic binary types supported by struct.unpack, but some have members that are themselves complex structs. So, I'm creating a pattern for handling these in which struct.unpack is called with a format string for the entire complex struct, and then slices of the returned values are passed to the member struct types to interpret.

For example, for a given table type—let's call it Foo, the combined format string would be '>HHH2H'. The method in Foo that handles parsing for that type knows that this is divided into two members, a uint16 followed by a struct. (So, the format string was a combination of ">H" and ">HH2H".) The call to struct.unpack returned a tuple of 5 elements. A slice of four of those elements will get passed to a method in the member type for it to interpret.

Here's this bit of code for one particular case:

    vals = struct.unpack(
        PaintFormat1._packedFormat,
        fileBytes[:PaintFormat1._packedSize]
        )

    color = ColorIndex.interpretUnpackedValues(vals[1:])

PaintFormat1._packedFormat is the format string for the entire complex structure, '>HHH2H'. fileBytes is a byte sequence that starts at the start of the PaintFormat1 structure.

Now, I've defined the ColorIndex.interpretUnpackedValues method with a signature as follows:

    def interpretUnpackedValues(*vals):

        assert len(vals) == ColorIndex._numUnpackedValues

ColorIndex._numUnpackedValues is the expected number of arguments. The problem is that vals[1:] (from the calling context) is getting passed as a tuple, not as elements of the tuple. So, for instance, in one call the arguments received are ((17, 0, 0, 0),), not (17, 0, 0, 0). Thus, len(vals) is 1, and the assert is tripped.

Another way to put this: I was expecting to write the assert using len(vals), not len(vals[0]). And then to access the individual values as vals[0], vals[1], etc., not vals[0][0], vals[0][1]...

How do I call so that multiple arguments are passed rather than a single tuple?

Or should I re-write the function definition to expect a single tuple and access member elements within that function?

(This will be a recurring pattern I use in many places, so I want to understand and keep the code as simple and readable as possible.)

Upvotes: 1

Views: 160

Answers (1)

sahinakkaya
sahinakkaya

Reputation: 6056

You can use * operator to unpack your arguments:

ColorIndex.interpretUnpackedValues(*vals[1:])

Upvotes: 2

Related Questions