Python way to repeat method call on different final arguments

I looked at a piece of Python code that I produced, which, although correct, is ugly. Is there a more pythonic way to do this?

r = self.get_pixel(x,y, RED) g = self.get_pixel(x,y, GREEN) b = self.get_pixel(x,y, BLUE) t = function(r,g,b) if t: r2, g2, b2 = t self.set_pixel(x,y,RED, r2) self.set_pixel(x,y,GREEN, g2) self.set_pixel(x,y,BLUE, b2) 

The problem is repeating the method call for get_pixel and set_pixel . For information:

  RED, GREEN, BLUE = range(3) 

Also note that I would like to keep the code clear and clean.

+6
python coding-style
source share
4 answers

To call a function with different arguments and collect the results, you can use list comprehension:

 r1, r2, r3 = [foo(x) for x in [x1, x2, x3]] 

To call a function for its side effects, I would recommend not using list comprehension and using a regular loop instead:

 ijs = [(i1, j1), (i2, j2), (i3, j3)] for i, j in ijs: bar(i, j) 

However, your problem is not that you should not name your set of pixels for each color separately. If at all possible, change your API so that you can do it as suggested by John Machin :

 old_colors = self.get_pixel_colors(x, y) new_colors = function(*old_colors) if new_colors: self.set_pixel_colors(x, y, *new_colors) 
+1
source share

As you use self , it seems that get_pixel , etc. are the methods of your class. Instead of understanding lists and zip() and other workarounds, look at the APIs and fix them. Two suggestions:

  • Write another get_pixel_colors(x, y) method that returns a 3-tuple. Then you can write r, g, b = self.get_pixel_colors(x, y)
  • Similarly: self.set_pixel_colors(x, y, r, g, b)

Even better, you can use the *args notation:

 old_colors = self.get_pixel_colors(x, y) new_colors = function(*old_colors) if new_colors: self.set_pixel_colors(x, y, *new_colors) 
+5
source share

I would use a named tuple to represent the color and change the class to use color attributes rather than a separate get_pixel(x,y,c) . For example:

 from collections import namedtuple Color = namedtuple('Color', 'red green blue') #... color = self.get_pixel(x, y) t = function(*color) if t: self.set_pixel(x, y, color) 

Edit : Thanks to John Machin for the corrections suggested here. His answer also provides more information on the reasons for this approach. I would add that a namedtuple gives the advantage of having fields like color.red , color.green , color.blue that I like. YMMV.

+4
source share
 colors = (RED, GREEN, BLUE) r, g, b = [self.get_pixel(x, y, col) for col in colors] t = function(r, g, b) for col, rgb in zip(colors, t): self.set_pixel(x, y, col, rgb) 
+1
source share

All Articles