Manuel Araoz
Manuel Araoz

Reputation: 16406

Pythonic way of repeating a method call on different finite arguments

I was staring at a piece of Python code I produced, which, though correct, is ugly. Is there a more pythonic way of doing 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 the repetition of the method calls for get_pixel and set_pixel. For your information:

    RED, GREEN, BLUE = range(3)

Also note that I'd like to preserve code clarity and cleanness.

Upvotes: 6

Views: 242

Answers (4)

Mark Byers
Mark Byers

Reputation: 838276

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

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

To call a function for its side-effects I'd recommend not using a list comprehension and instead using an ordinary for loop:

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

However your problem really is not that you should not be calling your set pixel for each color separately. If at all possible, change your API so that you can do this 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)

Upvotes: 1

Muhammad Alkarouri
Muhammad Alkarouri

Reputation: 24662

I would use a named tuple to represent the color, and change the class to use color attributes rather than individual 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 gives more insight into the reasons for this approach. I would add that a namedtuple gives the advantage of having fields such as color.red, color.green, color.blue which I like to have available. YMMV.

Upvotes: 4

John Machin
John Machin

Reputation: 82934

As you are using self, it appears that get_pixel etc are methods of your class. Instead of list comprehensions and zip() and other workarounds, look at the APIs and fix them. Two suggestions:

  1. Write another method get_pixel_colors(x, y) which returns a 3-tuple. Then you can write r, g, b = self.get_pixel_colors(x, y)
  2. 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)

Upvotes: 5

ars
ars

Reputation: 123518

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)

Upvotes: 1

Related Questions