Question

I feel this is a very simple question but I can't find to seem a proper answer. Basically I have a list of functions call from a class named simulation:

simulation.addGroup("teapotarmy")
simulation.populateGroup(20)
simulation.addNode("input",INPUT)
simulation.addNode("output",OUTPUT);
simulation.connectNodes("input","output");
simulation.manipOutputNode("output", "group.xvel");
simulation.manipInputNode("input", 1, 0.05);

Is there a way to call those functions without having to repeat the class name everytime? Something along the line of:

(thethingIwant) simulation:
    addGroup("teapotarmy")
    populateGroup(20)
    addNode("input",INPUT)
    ...

I have done this in other programming languages but I haven't figured out the syntax in Python. I have a faint memory of it having something to do with the 'with' statement...? Thanks in advance.

Leon

Was it helpful?

Solution

Simply, no. There is no (good, see my comment at the end) way to do this. The best you could do is assign it to another, shorter name:

s = simulation
s.addGroup("teapotarmy")
...

Which isn't too bad, although I'd argue the normal method is the one that is more readable.

As an extra, it's not strictly true that you can't do this. You could assign all of the methods of simulation to the local namespace programatically, however, this would be pretty confusing to follow, and I'd advise against it.

Example:

from contextlib import contextmanager
import inspect

class some_class:
    def test(self):
        print("test!")

@contextmanager
def map_to_local(inst, locals):
    methods = inspect.getmembers(inst, inspect.ismethod)
    for name, method in methods:
        locals[name] = method
    yield
    for name, method in methods:
        del locals[name]

inst = some_class()
with map_to_local(inst, locals()):
    test()

Note this is pretty fragile - you would have to be careful and do stuff like checking you are not overwriting values, checking values haven't been deleted before the context manager exits, etc... It's also pretty unclear what is going on.

tl;dr: Yes, it's possible, no, you shouldn't do it. Your current code is fine and clear.

OTHER TIPS

To work with your existing class as it is currently designed, the usual solution is to use a shorter variable name:

s = simulation
s.addGroup("teapotarmy")
s.populateGroup(20)
s.addNode("input",INPUT)
s.addNode("output",OUTPUT)
s.connectNodes("input","output")
s.manipOutputNode("output", "group.xvel")
s.manipInputNode("input", 1, 0.05)

That said, an alternative solution is to slightly alter the class to have these methods return self. Then you can write:

(simulation
    .addGroup("teapotarmy")
    .populateGroup(20) 
    .addNode("input",INPUT)
    .addNode("output",OUTPUT)
    .connectNodes("input","output")
    .manipOutputNode("output", "group.xvel")
    .manipInputNode("input", 1, 0.05))

The normal Python style is to have mutating methods return None (to provide a hint that mutation occurred); however, returning self is the norm for APIs such as yours where it is common to apply a succession of transformations and state updates.

The closest thing I can think of is taking advantage of the fact that Python's functions are also attributes of your class (callable attributes) and as such you can "get" them by name and call them...

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class Simulation(object):
  def addGroup(self, groupToAdd):
    print "Group to add: %s" % groupToAdd

  def addNode(self, inputType, inputChannel):
    print "My inputs: %s, channel: %s" % (inputType, inputChannel)

if __name__ == "__main__":
  simulation = Simulation()
  functionsToCall = [
      ("addGroup", "teapotarmy"),
      ("addNode", "input", "INPUT"),
    ]
  for functionToCall in functionsToCall:
    getattr(simulation, functionToCall[0])(* functionToCall[1:])

But this probably turns your code more confusing than before. If someone else has to modify your code, it may complicate his task... quite a bit. :)

For more info: Callables, packing parameters.

for each method you define in your class you can return the self object.

class schema:
    def __init__(self,name,age):
        self.name = name 
        self.age = age
    def giveName(self):
        print(self.name)
        return self
    def giveAge(self):
        print(self.age)
        return self

obj = schema(name="yo",age=15)
obj.giveName().giveAge()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top