Python Forum
Structuring a large class: privite vs public methods - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: Structuring a large class: privite vs public methods (/thread-39921.html)



Structuring a large class: privite vs public methods - 6hearts - May-05-2023

I am developing a bigger project that includes some data structures as classes and various functions/operations that computes with the structure of modifies them.

My dilemma is the following:

I have a bunch of methods inside the class that allow the users to interact with the structure,
but there are many common private methods that get called from the public methods,
lets call one _remove_something_from_structure() and _modify_something_from_structure() .

I don't want the users to call these directly, since they can be misused and can make the structure not make sense.

But then my package has a lot subpackages that contain functions that perform various operations and computations on the structure,
here I call the private methods _remove_something_from_structure() and _modify_something_from_structure() ,
since the functions need these operations. But my IDE is not happy about me accessing private methods.

I see two solutions:

1. ignore the IDE warnings. The functions are part of the package that contains the class, so I call private methods from outside, but the end used shold not access them.

2. Make all such methods public and allow the end user to call the methods which can render the whole instance non-sensical.

What do you guys think?


RE: Structuring a large class: privite vs public methods - Gribouillis - May-05-2023

I think you could use a facade pattern in this case. Instead of calling private methods of the class, you would call public methods on a private member of the class, so instead of
class A:
     def _private_spam_method(self):
          ...
     def method(self):
          self._private_spam_method()
you would have
class SubObject:
    def spam_method(self):
        ...

class A:
    def __init__(self):
        self._subobj = SubObject()

    def method(self):
        self._subobj.spam_method()
So you develop the subpackages that call private methods independently from the public class A. They would call directly spam_method() on SubObject instances.


RE: Structuring a large class: privite vs public methods - 6hearts - May-05-2023

I Like the direction, but perhaps I was not completely clear.

I am not calling private methods from the class, but outside of it:

class A:
     def _private_spam_method(self):
          ...

     def my_method(self):
          ....
          self._private_spam_method()

def new_modified_A(a: A):
     b = deepcopy(a)
     b._private_spam_method()
     return b
I could put new_modified_A inside the class, but since I have a bunch of modification methods, I want to put them ouside, so the class stays clean.


RE: Structuring a large class: privite vs public methods - Gribouillis - May-05-2023

In fact, you'd like some kind of "friend functions" and "friend classes" like in C++. There is no such thing in Python of course, because encapsulation is not strictly enforced.

I have a suggestion below that minimizes the encapsulation breach, and your IDE may not detect it
from copy import deepcopy

class SubObject: # contains A's 'private' interface
    def __init__(self):
        self.data = 10

    def spam_method(self):
        self.data = 20


class A:
    def __init__(self, subobject = None):
        self._subobj = subobject or SubObject()

    def method(self):
        self._subobj.spam_method()

    def value(self):
        return self._subobj.data

# Break encapsulation once. Use exec to trap the IDE
exec("""
def get_subobj(a):
    return a._subobj
""")

def new_modified_A(a):
    t = deepcopy(get_subobj(a))
    t.spam_method()
    b = A(subobject=t)
    return b

if __name__ == '__main__':
    a = A()
    b = new_modified_A(a)
    print(a, a.value())
    print(b, b.value())
Output:
λ python paillasse/pf/private.py <__main__.A object at 0x7fec4a32baf0> 10 <__main__.A object at 0x7fec4a359450> 20