2

I'm working around a physical problem, involving a collection of (physical) objects (let's call them B) an their relation to another (physical) object (let's call it A). Each B has individual properties, and the computation of these properties may depend on A's properties. A is unique and disconnected from A, the Bs have no purpose or meaning. Is there a way to access, from the class definition of B, the attributes of A which would be on the same "level" if an object B was to be instantiated from an instance of A? Something like

class A(object):
    def __init__(self, foo, bar, b1, b2):
        self.foo = foo
        self.bar = bar
        self.list_bs = [b1, b2]  # instances of B

class B(object):
    def computation(self):
        # something involving foo or bar, magically accessed

I feel like I have four options (... are for brevity and not the Python ellipsis):

  1. passing my instance of A to the constructor of B
    class A(object):
        def add(self, b):
            self.list_bs.append(b)
    
    class B(object):
        __init__(self, a, ...):
            self.a = a
            ...
            ...
    
        def compute1(self):
            # something involving self.a.foo
    
        def compute2(self):
            # something involving self.a.bar
    
    a = A()
    a.add(B(a, ...))
    a.add(B(a, ...))
    
  2. passing selected attributes of my instance of A to the constructor of B
    class A(object):
        def add(self, b):
            self.list_bs.append(b)
    
    class B(object):
        def __init__(self, a1, a2, ...):
            self.foo = a1
            self.bar = a2
            ...
            ...
    
        def compute1(self):
            # something involving self.foo
    
        def compute2(self):
            # something involving self.bar
    a = A()
    a.add(B(a.foo, a.bar, ...))
    a.add(B(a.foo, a.bar, ...))
    
  3. triggering all computation requesting A's attributes from A, even if they are more related to B
    def class A(object):
        def add(self, b):
            b.compute1(self.foo)
            b.compute2(self.bar)
            self.list_bs.append(b)
    
    class B(object):
        def compute1(self, a1):
            # something involving a1
    
        def compute2(self, a2):
            # something involving a2
    
  4. ditching A and using its attributes as class attributes for B
    class B:
        foo = a1
        bar = a2
    
        def compute1(self):
            # something involving B.foo
    
        def compute2(self):
            # something involving B.bar
    

Am I missing something else, or over thinking this? Which one of these options would be the lightest/quickest? Options 1 and 2 feel like a lot of duplicating, even if it's only references. Option 3 feels like it could be hard to maintain. Option 4 would make me merge some methods of A with nothing to do with B into B, so I'm not a big fan.

2
  • option 2 seems the cleanest if you can do it that way. Each referene is 8 bytes, hardly a big deal. You can get away with only one extra reference with option 1, if it's really that big of a deal. Are you actually memory limited? Because then you should probably just use __slots__ and the savings will be 10x over a couple refrenes Commented Jul 3, 2020 at 7:19
  • I am not currently memory limited as this project is in an early stage, but it could get there quickly enough. I did not know about slots, thanks for the pointer! Commented Jul 6, 2020 at 0:20

1 Answer 1

2

I sometimes prefer to separate logic from data container (I'm not claiming that's the faster way neither the better), especially when it comes down to opertate on different variables/istances. In this case I thik I would consider to use 3 classes instead of one so the data stay separate from the logic. If the data you are handling is simple this may be and overshot, but, apart on that:

class A(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar
        

class B(object):
    def __init__(self, ):
        # whatever_you need

# optionally, if you really like classes, 
# you can also think about wrapping the list in his own class:
class WrapperB():
    def __init__(self):
      self.b_list = []

    def add(b: B):
       self.b_list.append(b)

class BusinessLogic()
    @staticmethod
    def compute(foo, bar, b_list: WrapperB or list):
       # whatever you need

Suppose you don't have just 2 classis (A, and B), but a lot more: I think logic separation can be an option to consider.

EDIT: As pointed out, a class with a single static method it's not really a "class" (in the sense that you don't need to build a class for a single staticmethod). In this case you can consider implementing a simple function. In this example I used a class just because if you need something more from the logic you can expand it.

Sign up to request clarification or add additional context in comments.

2 Comments

A class with a single static method shouldn't be a class. It should just be function, but I agree, this is likely the fundamental refactor you would want.
A wrapper definitely makes sense. I'll work my head around it a bit but it should do the trick.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.