0

consider class

class Grid():
    def __init__(self,r,z,t):
        self.rgrid = r
        self.zgrid = z
        self.tgrid = t
        self.otherstuff = long_computation(r,t,z)

class Solution(Grid):
    def __init__(self,r,g):
        self.zones = r
        Grid.__init__(self,g.r,g.z,g.t)

g = Grid(my_r,my_z,my_t)
sol = Solution(r,g)

This creates what I want, except the "long_computation" is done twice. What would be a clean way to structure the classes that would work whether I called just Grid, or whether I also did the Solution step?

Thanks, J.

Tunneller
  • 381
  • 2
  • 13
  • 2
    Why does `Solution` inherit from `Grid`, and why does it take an existing `Grid` as an argument instead of `r`, `z`, and `t`? – user2357112 May 24 '17 at 23:47
  • what's the difference between `r` in `Grid.__init__` and in `Solution.__init__`? it seems like last one should be named `zones` – Azat Ibrakov May 25 '17 at 03:35
  • You can have a class variable `done_long_computation` which you'd set the first time `__init__` is executed, and `__init__` checks if `done_long_computation` is set. – boardrider May 25 '17 at 20:21

4 Answers4

1

You can have a method to perform calculation in parent class. Using this you can call the method and compute the stuff which takes long time when you want to calculate it.

class Grid():
    def __init__(self,r,z,t):
        self.rgrid = r
        self.zgrid = z
        self.tgrid = t

    def start_long_computation(self):
        self.otherstuff = long_computation(self.rgrid,self.tgrid,self.zgrid)

call the start_long_computation in Solution instance

class Solution(Grid):
    def __init__(self,r,my_r ,my_z ,my_t):
        self.zones = r
        Grid.__init__(self, my_r ,my_z ,my_t)
        Grid.start_long_computation(self)

You can now access otherstuff using self.otherstuff in Solution instance

or

class Grid():
    def __init__(self,r,z,t):
        self.rgrid = r
        self.zgrid = z
        self.tgrid = t

    def start_long_computation(self):
        return long_computation(self.rgrid,self.tgrid,self.zgrid)

class Solution():
    def __init__(self,r,g):
        self.zones = r
        self.otherstuff = g.start_long_computation()

g = Grid(my_r,my_z,my_t)
sol = Solution(r,g)
Harwee
  • 1,601
  • 2
  • 21
  • 35
  • Hi, is there a way to for Solution to know if otherstuff has been created already? Sometimes I will want to use Grid and have it run the long-computation without calling it as part of Solution. Sometimes I will want to call Grid without calling Solution. Maybe subclass() ... hm. I just saw __dict__.keys() – Tunneller May 25 '17 at 01:57
  • You can use self.__dict__ to see if the attribute exists for the class and calculate if necessary. – Harwee May 25 '17 at 06:55
1
  • IMO you should avoid any computations inside of __init__ and only do attributes assigning, so if you wanna do long_computation once and use its result you can do it outside of __init__ and pass as argument.

  • If you are not using Python 3 you probably should inherit you base class Grid from object type (in Python 3 all classes are inherited from object by default). And there is function named super and you probably should use it like super(Solution, self).__init__ (or just super().__init__ in Python 3) instead of writing Grid.__init__.

  • Passing Grid instance to initialize Solution object looks ugly, why not passing required Grid attributes? Also your example will not work since Grid objects doesn't have r, z, t fields, but rgrid, zgrid, tgrid. If you need to construct Solution instance from Grid you can write custom constructor (e.g. there are many of them in datetime.datetime type).

Considering all this remarks

class Grid(object):
    def __init__(self, r, t, z, otherstuff):
        self.rgrid = r
        self.tgrid = t
        self.zgrid = z
        self.otherstuff = otherstuff


class Solution(Grid):
    def __init__(self, zones, r, t, z, otherstuff):
        self.zones = zones
        super(Solution, self).__init__(r, t, z, otherstuff)

    @classmethod
    def from_grid(cls, zones, grid):
        return cls(zones, grid.rgrid, grid.tgrid, grid.zgrid, grid.otherstuff)


otherstuff = long_computation(my_r, my_t, my_z)
g = Grid(my_r, my_t, my_z, otherstuff)
sol = Solution.from_grid(r, g)
Azat Ibrakov
  • 9,998
  • 9
  • 38
  • 50
  • Thanks, for pointing me to super() ... and then discovering mysteries of old-style classes vs new. The classmethod device is also new to me and will be useful. In some more detail, my "otherstuff" is more like two dozen values: grid.o1, grid.o2, grid.o3, grid.o4, grid.o5,..... etc. I don't want to have to type all of those in the main code each time. But I can use the classmethod to keep the main code clean. Appreciate the update. – Tunneller May 25 '17 at 15:00
0

The quick, dirty way would be to add a "new" parameter to the initialization:

def long_computation(r, t, z):
    print "Oh, the despair of wasted time!", r, t, z

class Grid():
    def __init__(self,r,z,t, new_grid=True):
        self.rgrid = r
        self.zgrid = z
        self.tgrid = t
        if new_grid:
            self.otherstuff = long_computation(r,t,z)

class Solution(Grid):
    def __init__(self,r,g):
        self.zones = r
        Grid.__init__(self, g.rgrid, g.zgrid, g.tgrid, False)
        # Inherit long computation from parent.
        self.otherstuff = g.otherstuff

my_r = 77
my_z = 18
my_t = 6.2
r = 0.333

g = Grid(my_r,my_z,my_t)
sol = Solution(r,g)

Another possibility is to memoize longcomputation.

Does that help at all? I know it's not aesthetically satisfying, but it gets the job done.

Prune
  • 76,765
  • 14
  • 60
  • 81
0

Hm, this looks pretty ugly but builds on the above:

class Grid(object):
    def __init__(self,r,z,t):    
        self.rgrid = r
        self.zgrid = z
        self.tgrid = t       

        if "otherstuff" not  in self.__dict__.keys(): 
            self.otherstuff = self.long_computation(r,t,z)
        else:
            print "No Need to Call"

    def long_computation(ggg,r, t, z):
        return 123456

class Solution(Grid):
    def __init__(self,r,g):
        self.zones = r

    if "otherstuff" in g.__dict__.keys(): 
       self.otherstuff = g.otherstuff

    super(Solution, self).__init__(g.rgrid,g.zgrid,g.tgrid)
Tunneller
  • 381
  • 2
  • 13