Rodrigo Siqueira
Rodrigo Siqueira

Reputation: 375

Chain over iterable

I have a Python script that controls a list of imaginary machines that have to walk above the lines of a given image.

I have a Control class that keeps track of all the instances of Machine and updates the instance's position and rotation. Due to some imperfections in the image, some instances can and will get lost so that they need to have their position "manually" recalculated.

The problem is that I need to apply a method in all the instances selected and returned in a list object without breaking the chain of function calls.

This is what I have:

class Control:
    def __init__(self, instances):
        self.machine_instances = instances
        ...

    def get(self, elem = 0, end = None, step = 1):
        return machine_instances[elem : end : step]

    ...


class Machine:
    def __init__(self, x_position):
        ...

    def position_recalc(self):
        ...

    ...

This is how it must be used:

ml = [Machine(int(sys.argv[i])) for i in range(1, sys.argc)]
c = Control(ml)

...

c.get(2, 4).position_recalc()

OBS: The method Control.get() may have its return value and type changed.

Upvotes: 0

Views: 114

Answers (3)

Corley Brigman
Corley Brigman

Reputation: 12401

using a self return to simplify this a bit. this is one way to implement a modifiable interface:

class Control(object):
    def __init__(self, instances):
        self.focus = None
        self.instances = instances

    def get(self, start=None, end=None, slice=None):
        self.focus = (start, end, slice)
        return self

    def position_recalc(self):
        if self.focus == None:
            self.focus = (None, None, None)
        for i in self.instances.__getitem__(slice(*self.focus)):
            i.position_recalc()
        self.focus = None

c.get(2,4).position_recalc()

c.get(2,4) returns the same item, but sets a context for it, and returns the same object to the user, which is necessary so position_recalc has an object to work on. there's probably a general pattern for this (delegator?), this is an unusual form for it but still the same general pattern.

Upvotes: 0

Christian Tapia
Christian Tapia

Reputation: 34146

You could create a class SelectedMachines:

class SelectedMachines:
    def __init__(self, selected):
        self.selected_machines = selected
    def position_recalc(self):
        for machine in self.selected_machines:
            machine.someFunction()

So you can return a SelectedMachines object in Control.get():

class Control:
    def __init__(self, instances):
        self.machine_instances = instances
        ...

    def get(self, elem = 0, end = None, step = 1):
        return SelectedMachines(machine_instances[elem : end : step])

...

Where someFunction() is a method that applies recalculates position of one specific machine.

Upvotes: 1

John La Rooy
John La Rooy

Reputation: 304255

Looks like you need to return something like a list with a position_recalc method

class ListWithReCalc(list):
    def position_recalc(self):
        ...

class Control:
    def __init__(self, instances):
        self.machine_instances = instances
        ...

    def get(self, elem = 0, end = None, step = 1):
        return ListWithReCalc(machine_instances[elem : end : step])

Otherwise you can use composition

class ThingWithReCalc(object):
    def __init__(self, items):
        self.items = items()

    def position_recalc(self):
        for i in self.items():
            i.position_recalc()

class Control:
    def __init__(self, instances):
        self.machine_instances = instances
        ...

    def get(self, elem = 0, end = None, step = 1):
        return ListWithReCalc(machine_instances[elem : end : step])

Upvotes: 1

Related Questions