Return iterable of unpackable values that won't break client code if I start returning more data

StackOverflow https://stackoverflow.com/questions/13475294

  •  30-11-2021
  •  | 
  •  

Pregunta

Let's say you're writing an API you intend to make public. A function quux in your API returns a list or a generator of tuples, e.g. yield (foo, bar).

The client code would typically use it like this:

for foo, bar in quux(whatever):
    # do stuff with foo and bar

Now, suppose that in the future you might want to start returning baz together with foo and bar. You don't want to return it right now, because YAGNI until proven otherwise.

What would be the best way to (try to) make sure future changes like that won't break client code?

I know that Python 3 allows people to do stuff like for foo, bar, *idontcare in quux(whatever) and in Python 2 one could always write a utility function (to use like this for foo, bar in iterleft(quux(whatever), 2)):

def iterleft(iterable, limit):
    for item in iterable:
        yield item[:limit]

But I was wondering whether there was a better way to do stuff like this.

¿Fue útil?

Solución 2

Return a namedtuple instead of a regular tuple, and you don't need to unpack it. You can then later extend the returned tuple without affecting existing users of the API:

from collections import namedtuple

QuuxReturnValue = namedtuple('QuuxReturnValue', ('foo', 'bar'))

def quux(*args, **kw):
    while True:
        yield QuuxReturnValue(foo='foo', bar='bar')

and your API is consumed like:

for retval in quux():
    print retval.foo, retval.bar

If you later update the namedtuple to add a 3rd parameter 'baz' the above consumer is still going to work.

Otros consejos

The problem is that you have semantic data ("the first element of the tuple means x, the second element means y, ...") which you are not passing with the data.

A generic way to solve this is to return a dictionary {"foo": foo, "bar": bar, ...} of parameters. Of course, then you can't use tuple unpacking.

Otherwise, to preserve backwards compatibility you can't change quux. Instead, you can write quux_with_bar that yields (foo, bar, baz) tuples and then make quux just filter the first two elements of that.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top