21

I'm using Python since some times and I am discovering the "pythonic" way to code. I am using a lot of tuples in my code, most of them are polar or Cartesian positions.

I found myself writing this :

window.set_pos([18,8])

instead of this :

window.set_pos((18,8))

to get rid of the double parenthesis I found hard to read.

It seems that python is automatically doing the type conversion from list to tuple, as my code works properly.

But is it a good way to code ? Do you have any presentation tip I could use to write readable code ?

Thank you in advance for your surely enlightening answers.

Zoé Martin
  • 1,887
  • 1
  • 16
  • 28
  • I like the way you have written it as well, seems more well defined imo. – jamylak Jun 29 '12 at 12:21
  • 9
    Python is not "doing type conversion from list to tuple". This is [duck typing](http://en.wikipedia.org/wiki/Duck_typing) in action: presumably `window.set_pos` requires a sequence of two items, but not necessarily a 2-`tuple` (I say presumably since I don't know where `window.set_pos` is from or what it does). – Chris Jun 29 '12 at 12:22
  • 9
    Rewrite the function to take two arguments instead of one 2-tuple. – Cat Plus Plus Jun 29 '12 at 12:23

6 Answers6

25

I'd be careful deciding to eschew tuples in favor of lists everywhere. Have you ever used the dis module? Watch what Python is doing at the bytecode level when you make a list verses making a tuple:

>>> def f():
...     x = [1,2,3,4,5,6,7]
...     return x
... 
>>> def g():
...     x = (1,2,3,4,5,6,7)
...     return x
... 
>>> import dis
>>> dis.dis(f)
  2           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               2 (2)
              6 LOAD_CONST               3 (3)
              9 LOAD_CONST               4 (4)
             12 LOAD_CONST               5 (5)
             15 LOAD_CONST               6 (6)
             18 LOAD_CONST               7 (7)
             21 BUILD_LIST               7
             24 STORE_FAST               0 (x)

  3          27 LOAD_FAST                0 (x)
             30 RETURN_VALUE     
>>>
>>>   
>>> dis.dis(g)
  2           0 LOAD_CONST               8 ((1, 2, 3, 4, 5, 6, 7))
              3 STORE_FAST               0 (x)

  3           6 LOAD_FAST                0 (x)
              9 RETURN_VALUE   

Though it will probably never be an issue in a GUI application (as your example seems to be), for performance reasons you may want to be careful about doing it everywhere in your code.

K. Brafford
  • 3,755
  • 2
  • 26
  • 30
  • 1
    Thank you very much, you convinced me ! I really care about performance as my application is not a GUI but a game with a graphical GUI. You're bytecode detailed answer is really helpful to understand what is going on in the background (I did not know the dis module). – Zoé Martin Jun 29 '12 at 12:54
  • 1
    I just timed the two functions: the list version takes approx. double the time than the tuple version (2.1 vs 0.9 seconds for 10 mio. calls). For two-element collections, though, the differences is smaller: 1.6 vs. 0.9 seconds. – daniel kullmann Jun 29 '12 at 13:09
  • 2
    Ah, right -- I had forgotten that tuple literals are [folded](http://stackoverflow.com/a/8068248/577088). If you're passing literals (and not tuples constructed from variables), this could make a big difference. – senderle Jun 29 '12 at 13:49
12

You say

It seems that python is automatically doing the type conversion from list to tuple

That's doubtful. Since lists and tuples are both sequence types, they both implement many of the same behaviors, and whatever GUI library you're using must not need any of the list-only behaviors.

It's probably fine to do this in many cases, but note that lists do take up a bit more space than tuples:

>>> sys.getsizeof((1, 2))
72
>>> sys.getsizeof([1, 2])
88

And may be slower than tuples at some things:

>>> lst, tup = [1, 2], (1, 2)
>>> def unpack(x):
...     a, b = x
...     
>>> %timeit unpack(tup)
10000000 loops, best of 3: 163 ns per loop
>>> %timeit unpack(lst)
10000000 loops, best of 3: 172 ns per loop

These are very small differences that won't matter until you reach much larger scales -- as in billions of calls -- so the trade-off could be worth it.

Still, I don't see people do this very often. It seems like a nice readability trick, but it could have unexpected consequences in some circumstances. For example, if you pass a list you intend to use again later, then you have to be careful not to modify it inside the function. Finally, as J.F. Sebastian rightly points out, tuples and lists tend to mean slightly different things; using them in unconventional ways might negate the readability boost you seek.

Community
  • 1
  • 1
senderle
  • 145,869
  • 36
  • 209
  • 233
  • Thank you for your precise answer ! I know it may seems a little odd, but I am thrilled by the idea to use an inappropriate object only for readability purpose. My "set_pos" method just unpack the argument into two variables, but is used most of the time inside a loop and I care about performance issues. Do 14 ns matter a lot ? – Zoé Martin Jun 29 '12 at 12:49
  • No, 14ns won't matter at all when it comes to setting the position of your window. – K. Brafford Jun 29 '12 at 12:54
8

I seriously doubt that tuples vs. lists make noticable difference in performance in your case. Don't do micro-optimizations unless your profiler says so. Readability is a priority.

list and tuple are both sequence types.

Semantically tuples might be preferable (see Tuples have structure, lists have order). Position is a single object that has x, y attributes.

Visually lists might be easier to read in this case.

Whatever you choose just be consistent.

Check whether window supports the following:

window.pos = 18, 8

A possible way to implement it is to make the pos a property and bind set_pos() as a setter.

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • 2
    +1 for the semantics of tuples vs. lists, which should almost always be the deciding factor in which to use. – FogleBird Jun 29 '12 at 13:49
4

IMO the only advantage of using (18,8) over [18,8] is that a tuple object is immutable and you can be sure that it'll not be changed inside the function it is passed, while [18,8] can be changed easily.

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
4

Python is not doing any automatic conversion from list to tuple, unless the set_pos method explicitly does it. tuple and list have very similar protocols, so they tend to be interchangeable within that protocol. You may still break things, in particular because the tuple is an immutable which can be hashed, and a list isn't.

To answer your question, just write

window.set_pos( (18,18) )

proper spaces do miracles with coding style.

Stefano Borini
  • 138,652
  • 96
  • 297
  • 431
  • 4
    PEP-8 advises NOT doing this: http://www.python.org/dev/peps/pep-0008/#pet-peeves – jamylak Jun 29 '12 at 12:31
  • 1
    @jamylak: agreed, and I agree with the PEP suggestion, but this is a special case that may be worth the exception. – Stefano Borini Jun 29 '12 at 12:34
  • @jamylak It's not enforced by the language, which means it's just a coding style that the author(s) of Python happen to be fond of. It's not the be-all-end-all of coding style, and should _never_ be taken as such. Just look at them as _good guidelines_, and be prepared to break them when they hurt readability - such as in this case. – Izkata Jun 29 '12 at 18:01
0

If you want to stress the fact that your parameter is a tuple, you can explicity convert it:

window.set_pot(tuple([18, 18]))
user278064
  • 9,982
  • 1
  • 33
  • 46