3

How might I go about simplifying this code, preferably putting it into a loop or one line?

object1.callMethod()  
object2.callMethod()  
object3.callMethod()  

difObject1.callDifMethod()  
difObject2.callDifMethod()  
difObject3.callDifMethod()  

I know that I can put the objects in a list and iterate through that, however, that would still require two separate loops and two separate lists. Is there any way that I can make a singular list of [1,2,3] and use that to distinguish between the three different objects of each type, since the numbers are in the object names as well?

  • 2
    There are ways to do the kind of thing you're asking about but could you show real code for these classes/functions? That will help inform useful answers. – SuperBiasedMan Nov 07 '18 at 17:24
  • This is the only style of simplification that I'm looking for -- if this is doable, that is all I need. If I were to put all the code for these classes and functions here, it would be hundreds of lines. – Jonah Yousif Nov 07 '18 at 17:33
  • You may also be interested in [`operator.methodcaller`](https://docs.python.org/3/library/operator.html#operator.methodcaller) – Patrick Haugh Nov 07 '18 at 18:33
  • Related: [How do I create variable variables?](/q/1373164/4518341), esp [TigerhawkT3's answer](/a/38972761/4518341). TLDR: Use a list in the first place. – wjandrea Aug 05 '23 at 16:48

3 Answers3

2

You could use a dictionary approach:

methods = {cls1.method1: [cls1_obj1, cls1_obj2, cls1_obj3],
           cls1.method2: [cls1_obj4, cls1_obj5, cls1_obj6],
           cls2.method1: [cls2_obj1, cls2_obj2}

for method, objs in methods.items():
    for obj in objs:
        method(obj)

This assumes you are using an instance method though. For a static/class method you'll need to adjust the object being passed for the method.

I'm not sure there's anything elegant that doesn't involve predefining multiples (or combinations) of lists and dicts and loop over it, since you would need to be explicit in which object runs which methods, a definition is required either way.


Ideally, if you have multiple similar objects of the same class, you might opt to instantiate them in a list from the get go:

# Instead of this
object1 = Foo(1)
object2 = Foo(2)
object3 = Foo(3)
...

# do this
foos = [Foo(i) for i in range(3)]

# Or this
bars = {name: Bar(name) for name in list_of_names}

Then it becomes trivial to manipulate them in group:

for foo in foos:
    foo.foo_method()

for bar in bars.values():
    bar.bar_method()

While still easy to reference the object on its own:

foo[index].foo_method()
bar[key].bar_method()
r.ook
  • 13,466
  • 2
  • 22
  • 39
1
getattr(obj, method_name)()

If all of the method and object names are generally semantic, you can use getattr to reference the method based on a string variable, and then call it with ().

objects = [object1, object2, object3]
for obj in objects:
    getattr(obj, method_name)()

If you want to store the objects/method in parallel, use zip.

objects = [object1, object2, object3]
methods = ['method1name', 'method2name', 'method3name']
for obj, method in zip(objects, methods):
    getattr(obj, method)()
wjandrea
  • 28,235
  • 9
  • 60
  • 81
glotchimo
  • 664
  • 5
  • 23
-2

You could use the eval function:

for i in range(1, 4):
    eval("object%d" % i).callMethod()
    eval("difObject%d" % i).callDifMethod()
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Tim
  • 2,756
  • 1
  • 15
  • 31
  • I wouldn't use this. It does work, [but it makes the code messy](/a/1834754/4518341). And if someone applied it to a different case, where `i` comes from unsafe user input, [it's a security risk](//nedbatchelder.com/blog/201206/eval_really_is_dangerous.html). I think adding a warning to this answer would help. – wjandrea Aug 05 '23 at 17:04
  • 1
    There is no reason to suggest `eval` when safer, more specific alternatives are available. – chepner Aug 05 '23 at 17:06