Nice solution wanted: Hide internal interfaces

Discussion in 'Python' started by Johannes Bauer, Oct 29, 2012.

  1. Hi there,

    I'm currently looking for a good solution to the following problem: I
    have two classes A and B, which interact with each other and which
    interact with the user. Instances of B are always created by A.

    Now I want A to call some private methods of B and vice versa (i.e. what
    C++ "friends" are), but I want to make it hard for the user to call
    these private methods.

    Currently my ugly approach is this: I delare the internal methods
    private (hide from user). Then I have a function which gives me a
    dictionary of callbacks to the private functions of the other objects.
    This is in my opinion pretty ugly (but it works and does what I want).

    I'm pretty damn sure there's a nicer (prettier) solution out there, but
    I can't currently think of it. Do you have any hints?

    Best regards,
    Joe


    --
    >> Wo hattest Du das Beben nochmal GENAU vorhergesagt?

    > Zumindest nicht öffentlich!

    Ah, der neueste und bis heute genialste Streich unsere großen
    Kosmologen: Die Geheim-Vorhersage.
    - Karl Kaos über Rüdiger Thomas in dsa <hidbv3$om2$>
     
    Johannes Bauer, Oct 29, 2012
    #1
    1. Advertising

  2. On Mon, Oct 29, 2012 at 9:33 AM, Johannes Bauer <> wrote:
    > Hi there,
    >
    > I'm currently looking for a good solution to the following problem: I
    > have two classes A and B, which interact with each other and which
    > interact with the user. Instances of B are always created by A.
    >
    > Now I want A to call some private methods of B and vice versa (i.e. what
    > C++ "friends" are), but I want to make it hard for the user to call
    > these private methods.
    >
    > Currently my ugly approach is this: I delare the internal methods
    > private (hide from user). Then I have a function which gives me a
    > dictionary of callbacks to the private functions of the other objects.
    > This is in my opinion pretty ugly (but it works and does what I want).
    >
    > I'm pretty damn sure there's a nicer (prettier) solution out there, but
    > I can't currently think of it. Do you have any hints?
    >
    > Best regards,
    > Joe
    >


    What do you mean "declare the internal methods private"? Python
    doesn't have this notion of restricted access. By convention, names
    starting with a leading underscore are private, but it's not enforced
    by the language.
     
    Benjamin Kaplan, Oct 29, 2012
    #2
    1. Advertising

  3. 2012/10/29 Johannes Bauer <>:
    > Hi there,
    >
    > I'm currently looking for a good solution to the following problem: I
    > have two classes A and B, which interact with each other and which
    > interact with the user. Instances of B are always created by A.
    >
    > Now I want A to call some private methods of B and vice versa (i.e. what
    > C++ "friends" are), but I want to make it hard for the user to call
    > these private methods.
    >
    > Currently my ugly approach is this: I delare the internal methods
    > private (hide from user). Then I have a function which gives me a
    > dictionary of callbacks to the private functions of the other objects.
    > This is in my opinion pretty ugly (but it works and does what I want).
    >
    > I'm pretty damn sure there's a nicer (prettier) solution out there, but
    > I can't currently think of it. Do you have any hints?
    >
    > Best regards,
    > Joe
    >


    And how are you declaring methods private? Because there is no real
    private attribute in Python, if you declare them with a starting "_"
    they are still perfectly accessible..
     
    andrea crotti, Oct 29, 2012
    #3
  4. On Tue, Oct 30, 2012 at 3:33 AM, Johannes Bauer <> wrote:
    > Hi there,
    >
    > I'm currently looking for a good solution to the following problem: I
    > have two classes A and B, which interact with each other and which
    > interact with the user. Instances of B are always created by A.
    >
    > Now I want A to call some private methods of B and vice versa (i.e. what
    > C++ "friends" are), but I want to make it hard for the user to call
    > these private methods.


    The usual convention for private methods is a leading underscore on the name:

    class A:
    def foo(self):
    print("Fooing!")
    def _bar(self):
    print("Only my friends may bar me.")

    It's only a convention, though; it doesn't make it "hard" to call
    them, it just sends the message "this is private, I don't promise that
    it'll be stable across versions".

    Incidentally, you may want to use a nested class, if the definition of
    B is entirely dependent on A. Something like this:

    class A:
    class B:
    def _asdf(self,newval=None):
    if newval is not None: self._val=newval
    return self._val
    def _qwer(self,parent):
    parent._bar("My value is: "+self._val)
    def foo(self):
    self.b=self.B()
    self.b._asdf("Hello, world!")
    self.b._qwer(self)
    def _bar(self,msg):
    print("Message from internal: "+msg)

    ChrisA
     
    Chris Angelico, Oct 29, 2012
    #4
  5. On 2012-10-29, Johannes Bauer <> wrote:

    > I'm currently looking for a good solution to the following problem: I
    > have two classes A and B, which interact with each other and which
    > interact with the user. Instances of B are always created by A.
    >
    > Now I want A to call some private methods of B and vice versa (i.e.
    > what C++ "friends" are), but I want to make it hard for the user to
    > call these private methods.


    The Python way of telling a user not to call certain methods is to
    prefix their names with an underscore. The double-underscore thing
    that munges the names is just telling that a bit louder -- but they're
    still free to ignore that advice.

    > Currently my ugly approach is this: I delare the internal methods
    > private (hide from user). Then I have a function which gives me a
    > dictionary of callbacks to the private functions of the other
    > objects. This is in my opinion pretty ugly (but it works and does
    > what I want).


    By "decleare them privide" do you mean using __ASDF__ name-munging?

    It sounds to me like you're just making life hard on yourself.

    > I'm pretty damn sure there's a nicer (prettier) solution out there,
    > but I can't currently think of it. Do you have any hints?


    IMO, the "right" thing to do in Python is to use single underscore
    names for methods that you intend to be called by "friend" modules (is
    that correct C++ lingo?) but don't intend to be called by the
    end-user.

    --
    Grant Edwards grant.b.edwards Yow! I just had a NOSE
    at JOB!!
    gmail.com
     
    Grant Edwards, Oct 29, 2012
    #5
  6. On 29.10.2012 17:47, Chris Angelico wrote:

    > The usual convention for private methods is a leading underscore on the name:


    Yup, that's what I'm using.

    > It's only a convention, though; it doesn't make it "hard" to call
    > them, it just sends the message "this is private, I don't promise that
    > it'll be stable across versions".


    Yes, I know. But it's good enough. I don't want to restrict the use
    under all circumstances, just make it clear to the user what she is
    supposed to use and what not.

    > Incidentally, you may want to use a nested class, if the definition of
    > B is entirely dependent on A. Something like this:


    Ah, that's nice. I didn't know that nested classes could access their
    private members naturally (i.e. without using any magic, just with plain
    old attribute access).

    This makes the source files largish however (they're currently split up
    in different files). Can I use the nested class advantage and somehow
    include the inner class from another file?

    Best regards,
    Joe

    --
    >> Wo hattest Du das Beben nochmal GENAU vorhergesagt?

    > Zumindest nicht öffentlich!

    Ah, der neueste und bis heute genialste Streich unsere großen
    Kosmologen: Die Geheim-Vorhersage.
    - Karl Kaos über Rüdiger Thomas in dsa <hidbv3$om2$>
     
    Johannes Bauer, Oct 29, 2012
    #6
  7. On 29.10.2012 17:52, Grant Edwards wrote:

    > By "decleare them privide" do you mean using __ASDF__ name-munging?
    >
    > It sounds to me like you're just making life hard on yourself.


    Gaaaaaah, you are right. I just noticed that using the single underscore
    (as I do) does not restrict usage in any "non-natural" way. I was
    actually mixing this up in my head with the __xyz__ name-munging.

    Thank you very much for hitting my head with a brick. Much clearer now :)

    Best regards,
    Johannes

    --
    >> Wo hattest Du das Beben nochmal GENAU vorhergesagt?

    > Zumindest nicht öffentlich!

    Ah, der neueste und bis heute genialste Streich unsere großen
    Kosmologen: Die Geheim-Vorhersage.
    - Karl Kaos über Rüdiger Thomas in dsa <hidbv3$om2$>
     
    Johannes Bauer, Oct 29, 2012
    #7
  8. Johannes Bauer

    Paul Rubin Guest

    Johannes Bauer <> writes:
    > This makes the source files largish however (they're currently split up
    > in different files). Can I use the nested class advantage and somehow
    > include the inner class from another file?


    You could possibly duck-punch class A:

    import B

    class A:
    ...

    A.B = B.B

    Disclaimer: I haven't tested the above and I'd consider it pretty ugly.
     
    Paul Rubin, Oct 29, 2012
    #8
  9. Johannes Bauer

    Peter Otten Guest

    Johannes Bauer wrote:

    > Now I want A to call some private methods of B and vice versa (i.e. what
    > C++ "friends" are), but I want to make it hard for the user to call
    > these private methods.
    >
    > Currently my ugly approach is this: I delare the internal methods
    > private (hide from user). Then I have a function which gives me a
    > dictionary of callbacks to the private functions of the other objects.
    > This is in my opinion pretty ugly (but it works and does what I want).
    >
    > I'm pretty damn sure there's a nicer (prettier) solution out there, but
    > I can't currently think of it. Do you have any hints?


    Maybe you can wrap A into a class that delegates to A:

    >>> class A(object):

    .... def __private(self): print "private A"
    ....
    >>> class Friend(object):

    .... def __init__(self, obj):
    .... self.__obj = obj
    .... self.__prefix = "_%s_" % obj.__class__.__name__
    .... def __getattr__(self, name):
    .... return getattr(self.__obj, self.__prefix + name)
    ....
    >>> a = A()
    >>> a._A__private() # hard

    private A
    >>> f = Friend(a)
    >>> f._private() # easy

    private A

    The B instance would refer to A via the Friend instance.
     
    Peter Otten, Oct 29, 2012
    #9
  10. Johannes Bauer

    Peter Otten Guest

    Johannes Bauer wrote:

    > On 29.10.2012 17:52, Grant Edwards wrote:
    >
    >> By "decleare them privide" do you mean using __ASDF__ name-munging?
    >>
    >> It sounds to me like you're just making life hard on yourself.

    >
    > Gaaaaaah, you are right. I just noticed that using the single underscore
    > (as I do) does not restrict usage in any "non-natural" way. I was
    > actually mixing this up in my head with the __xyz__ name-munging.


    Name-munging occurs only if the method name starts but doesn't end with
    "__":

    >>> class A(object): pass

    ....
    >>> class B(A):

    .... __before = __between__ = __ = 42
    ....
    >>> set(dir(B)) - set(dir(A))

    set(['__', '_B__before', '__between__'])
     
    Peter Otten, Oct 29, 2012
    #10
  11. On 2012-10-29, Johannes Bauer <> wrote:
    > On 29.10.2012 17:47, Chris Angelico wrote:
    >
    >> The usual convention for private methods is a leading underscore on the name:

    >
    > Yup, that's what I'm using.
    >
    >> It's only a convention, though; it doesn't make it "hard" to call
    >> them, it just sends the message "this is private, I don't promise that
    >> it'll be stable across versions".

    >
    > Yes, I know. But it's good enough. I don't want to restrict the use
    > under all circumstances, just make it clear to the user what she is
    > supposed to use and what not.


    The single underscore indicates that the user is not to use the
    method.

    --
    Grant Edwards grant.b.edwards Yow! I am covered with
    at pure vegetable oil and I am
    gmail.com writing a best seller!
     
    Grant Edwards, Oct 29, 2012
    #11
  12. Johannes Bauer

    Ian Kelly Guest

    On Mon, Oct 29, 2012 at 10:58 AM, Johannes Bauer <> wrote:
    > Ah, that's nice. I didn't know that nested classes could access their
    > private members naturally (i.e. without using any magic, just with plain
    > old attribute access).


    There is nothing at all special about nested classes that is different
    from non-nested classes. All it affects is organization; instead of
    classes A and B, you have classes A and A.B.
     
    Ian Kelly, Oct 29, 2012
    #12
  13. On Mon, 29 Oct 2012 17:33:24 +0100, Johannes Bauer wrote:

    > Hi there,
    >
    > I'm currently looking for a good solution to the following problem: I
    > have two classes A and B, which interact with each other and which
    > interact with the user. Instances of B are always created by A.
    >
    > Now I want A to call some private methods of B and vice versa (i.e. what
    > C++ "friends" are), but I want to make it hard for the user to call
    > these private methods.


    In B, name these private methods with a leading underscore, exactly as
    you would for any other private name, e.g. B._method.

    Do not document the existence of B._method in external documentation
    aimed at the user, except to note that all methods with leading
    underscores are private, and the use of them is unsupported and subject
    to change without notice.

    In A, use B._method normally. After all, it's *your* code, you can do
    whatever you see fit.


    > Currently my ugly approach is this: I delare the internal methods
    > private (hide from user). Then I have a function which gives me a
    > dictionary of callbacks to the private functions of the other objects.
    > This is in my opinion pretty ugly (but it works and does what I want).


    Seems to me that this dictionary of callbacks does nothing but add
    unnecessary complexity to your code. What's the point of it?

    Besides, if your users are foolish enough to use flagged private
    _methods, they're foolish enough to access the functions in the callback
    dictionary. So you gain nothing but extra work.



    --
    Steven
     
    Steven D'Aprano, Oct 29, 2012
    #13
  14. Johannes Bauer

    alex23 Guest

    On Oct 30, 2:33 am, Johannes Bauer <> wrote:
    > I'm currently looking for a good solution to the following problem: I
    > have two classes A and B, which interact with each other and which
    > interact with the user. Instances of B are always created by A.
    >
    > Now I want A to call some private methods of B and vice versa (i.e. what
    > C++ "friends" are), but I want to make it hard for the user to call
    > these private methods.


    One approach could be to only have the public interface on B, and then
    create a wrapper for B that provides the private interface:

    class B:
    def public_method(self):
    pass

    class B_Private:
    def __init__(self, context):
    self.context = context

    def private_method(self):
    # manipulate self.context

    class A:
    def __init__(self):
    self.b = B()
    self.b_private = B_Private(self.b)

    def foo(self):
    # call public method
    self.b.public_method()

    # call private method
    self.b_private.private_method()

    It doesn't stop a user from accessing the private methods, but it does
    separate them so they have to *intentionally* choose to use them.
     
    alex23, Oct 30, 2012
    #14
  15. 2012/10/30 alex23 <>:
    > On Oct 30, 2:33 am, Johannes Bauer <> wrote:
    >> I'm currently looking for a good solution to the following problem: I
    >> have two classes A and B, which interact with each other and which
    >> interact with the user. Instances of B are always created by A.
    >>
    >> Now I want A to call some private methods of B and vice versa (i.e. what
    >> C++ "friends" are), but I want to make it hard for the user to call
    >> these private methods.

    >
    > One approach could be to only have the public interface on B, and then
    > create a wrapper for B that provides the private interface:
    >
    > class B:
    > def public_method(self):
    > pass
    >
    > class B_Private:
    > def __init__(self, context):
    > self.context = context
    >
    > def private_method(self):
    > # manipulate self.context
    >
    > class A:
    > def __init__(self):
    > self.b = B()
    > self.b_private = B_Private(self.b)
    >
    > def foo(self):
    > # call public method
    > self.b.public_method()
    >
    > # call private method
    > self.b_private.private_method()
    >
    > It doesn't stop a user from accessing the private methods, but it does
    > separate them so they have to *intentionally* choose to use them.
    > --
    > http://mail.python.org/mailman/listinfo/python-list




    Partly unrelated, but you could also define a clear API and expose it
    through your __init__.py.

    For example:
    package/a.py:
    class A: pass

    package/b.py:
    class B:pass

    package/__init__.py
    from a import A

    so now doing "from package import" will only show A.

    This doesn't work on the method-level, but it's useful to know and
    commonly done in many projects..


    In some projects they even use a file "api.py" to you have to
    explicitly import

    from package.api import ..
    (which I think is overkill since __init__.py does the same)
     
    andrea crotti, Oct 30, 2012
    #15
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Harvey
    Replies:
    0
    Views:
    777
    Harvey
    Jul 16, 2004
  2. Harvey
    Replies:
    1
    Views:
    892
    Daniel
    Jul 16, 2004
  3. Claudio Grondi
    Replies:
    2
    Views:
    371
    Claudio Grondi
    Jul 3, 2006
  4. Paul McGuire
    Replies:
    3
    Views:
    552
    Paul McGuire
    Nov 7, 2008
  5. Curt Hibbs
    Replies:
    6
    Views:
    160
    Curt Hibbs
    Mar 19, 2005
Loading...

Share This Page