Nice solution wanted: Hide internal interfaces

J

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


--
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 <[email protected]>
 
B

Benjamin Kaplan

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.
 
A

andrea crotti

2012/10/29 Johannes Bauer said:
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..
 
C

Chris Angelico

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
 
G

Grant Edwards

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.
 
J

Johannes Bauer

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

--
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 <[email protected]>
 
J

Johannes Bauer

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

--
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 <[email protected]>
 
P

Paul Rubin

Johannes Bauer said:
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.
 
P

Peter Otten

Johannes said:
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:
.... def __private(self): print "private A"
........ def __init__(self, obj):
.... self.__obj = obj
.... self.__prefix = "_%s_" % obj.__class__.__name__
.... def __getattr__(self, name):
.... return getattr(self.__obj, self.__prefix + name)
....private A

The B instance would refer to A via the Friend instance.
 
P

Peter Otten

Johannes said:
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
"__":
.... __before = __between__ = __ = 42
....set(['__', '_B__before', '__between__'])
 
G

Grant Edwards

Yup, that's what I'm using.


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.
 
I

Ian Kelly

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.
 
S

Steven D'Aprano

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.
 
A

alex23

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.
 
A

andrea crotti

2012/10/30 alex23 said:
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.



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)
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top