Home > Back-end >  Passing attributes and methods of object inside another object
Passing attributes and methods of object inside another object

Time:02-19

I am writing an API in Python 3.9. I'll first provide a simplified example of what I'm working on and below the code will expand a bit on what I'm trying to implement from a design perspective. I just started getting into more complex design and object composition things, so it's entirely possible that some of what I'm trying to do is either not possible or not best practice - I'd be interested to know what I can improve.

In primary_module.py:

from some_module import do_something
from secondary_module import SecondaryClass

class PrimaryClass:
    def __init__(self, param1, param2):
        self.attr1 = param1
        self._attr2 = do_something(param2)
    def primary_method(self):
        sec_cls = SecondaryClass(PrimaryClass)
        sec_cls.secondary_method()

In secondary_module.py:

class SecondaryClass:
    def secondary_method(pri_cls):
        print(pri_cls.attr1)
        print(pri_cls._attr2)

As you can see, I have one class SecondaryClass defined in secondary_module.py and am using that to compose a class called PrimaryClass in primary_module.py. From an API standpoint, I would like end users to only directly access PrimaryClass and never let them see SecondaryClass even though it is being used under the hood. The only time SecondaryClass needs to be instantiated is when running PrimaryClass.primary_method() and related objects, so there's no situation in which SecondaryClass would run as a standalone object. However, when running this method, SecondaryClass needs access to some of the user-specified data contained in PrimaryClass. Keeping in mind that SecondaryClass is (theoretically) never directly accessed by end users, what's the best way to allow SecondaryClass to see certain attributes of PrimaryClass? Is it acceptable to use duck typing in the definition of SecondaryClass.secondary_method() to grant it access to certain attributes (both public and hidden) of PrimaryClass? Or is there some better solution that I'm not thinking of?

CodePudding user response:

It's not totally clear, but I think this is what you want:

class PrimaryClass:
    def __init__(self, param1, param2):
        self.attr1 = param1
        self._attr2 = do_something(param2)
    def primary_method(self):
        sec_cls = SecondaryClass(self)
        sec_cls.secondary_method()
    def __private_method(self):
        do_something_else()

class SecondaryClass:
    def __init__(self, primary):
        self.primary = primary

    def secondary_method():
        print(self.primary.attr1)
        print(self.primary._attr2)

To hide SecondaryClass from users of primary_module, use:

from secondary_module import SecondaryClass as _SecondaryClass

See Hide external modules when importing a module (e.g. regarding code-completion)

CodePudding user response:

It's hard to discuss this without understanding what the classes are actually doing.

That said, it's generally not a good idea to have two classes mutually depending on each other as this tends to generate brittle code: changes in either class will potentially break both classes and whoever depends on either of them.

It'd be better to pass secondary_method what it needs explicitly (i.e. secondary_method(self._attr2)), possibly as a function pointer (e.g. lambda: __private_method(), lambda: self.attr1), either through the class constructor (i.e. __init__) or as a method parameter for secondary_method. This way, SecondaryClass doesn't depend on PrimaryClass specifically and the dependencies from secondary_method and SecondaryClass are made explicit.

class PrimaryClass:
    def __init__(self, param1, param2):
        self.attr1 = param1
        self._attr2 = do_something(param2)

    def primary_method(self):
        # E.g. 1: Pass _attr2 via constructor
        sec_cls = SecondaryClass(self._attr2)
        # E.g. 2 3: Pass attr1 and _attr2 as method parameters
        sec_cls.secondary_method(
            lambda: self.attr1,
            lambda: __private_method(),
        )

    def __private_method(self):
        do_something_else()

class SecondaryClass:
    def __init__(self, attr2):
        self.attr2 = attr2

    def secondary_method(get_attr1, private_callback):
        print(self.attr2)    # <- _attr2, received from constructor
        print(get_attr1())  # <- Getting attr1 at execution time from lambda
        private_callback()    # <- Calling __private_method
  • Related