3

I'm reading tab separated values from strings into an object like this:

class Node(rect):
    def __init__(self, line):
        (self.id, self.x1, self.y1, self.x2, self.y2) = line.split('\t')

That works fine, but say I want to convert those x and y coordinates, which are read from the string line, to floats. What is the most pythonic way to do this? I imagine something like

(self.id, float(self.x1), float(self.y1), float(self.x2), float(self.y2)) = line.split('\t')

which of course does not work. Is there an elegant way to do this or do I have to manually convert afterwards like self.x1 = float(self.x1)?

gietljohannes
  • 305
  • 1
  • 8

3 Answers3

7

You can't do that on one line but you can do something like:

self.id, *rest = line.split('\t')
self.x1, self.y1, self.x2, self.y2 = map(float, rest)

If you are on python2 then you have to do:

splitted = line.split('\t')
self.id = splitted.pop(0)
self.x1, self.y1, self.x2, self.y2 = map(float, splitted)
Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • Wow, I've never heard of *assigning* to something like `*rest`. Awesome! – mkrieger1 May 04 '15 at 06:38
  • @mkrieger1 It's a new python3 only feature. See [this](http://stackoverflow.com/questions/6967632/unpacking-extended-unpacking-and-nested-extended-unpacking) question about it, the [PEP 3132](https://www.python.org/dev/peps/pep-3132/) that defines it. I believe in newer versions of python we will soon see an extension to it (see [PEP 448](https://www.python.org/dev/peps/pep-0448/) which should land in python3.5). – Bakuriu May 04 '15 at 06:41
3

I've asked this myself many times.

The best way I came up with was to define a list of input conversion functions and then zipping them with the arguments:

identity = lambda x: x
input_conversion = [identity, float, float, float, float]
self.id, self.x1, self.y1, self.x2, self.y2 = (
  f(x) for f, x in zip(input_conversion, line.split('\t')))
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
  • 1
    For more complex cases, this is a useful trick to have in your toolbox. By the way, you can do this with a generator expression rather than a list comprehension (parens instead of square brackets). I don't think that will make any difference for performance (you're not going to unpack 5000 values… plus, I think at least CPython is going to create a list anyway), but conceptually you don't need a list, so why ask for one? – abarnert May 04 '15 at 07:00
  • Great solution, albeit a little overkill for my use case. I'll keep it in mind for later though! – gietljohannes May 04 '15 at 07:12
  • 1
    I have to admit that I rarely used this myself. It usually was a hint to redesign the code so that the original situation is avoided in the first place... – mkrieger1 May 04 '15 at 07:14
  • I've done this with `csv` files. Although the two times I remember doing it, I ended up rewriting it to use a subclass of `csv.DictReader` and a map from names to types instead of just zipping `csv.reader` and a tuple of types, to debug some stupid problem where I counted the columns wrong… – abarnert May 04 '15 at 07:18
1

You can't do anything like what you're trying to do. But there are options.

In this case, you're trying to convert everything but the first value to a float. You could do that like this:

bits = line.split('\t')
self.id = bits.pop()
self.x1, self.y1, self.x2, self.y2 = map(float, bits)
abarnert
  • 354,177
  • 51
  • 601
  • 671