Reputation: 170856
I know that there is no such thing as true private in Python for sometimes you need to following:
annotations
? Please remember that this has to work with existing codebase, so it should support a gradually re factoring of the existing code.
The last point is almost solved, the only question about it is if I should use one underscore or two underscores?
Upvotes: 0
Views: 357
Reputation: 22768
Hiding from autocomplete is going to completely depend on your editor and how it handles autocomplete. My editor does not do autocomplete so I wouldn't need to use it.
The standard python convention is to prefix and underscore to the method name. This tells the user that the method is private and shouldn't be used.
You can use double underscores before the method name; this will invoke name mangling.
See 9.7 on this page for details: http://docs.python.org/tutorial/classes.htm.
But it's still not private; it can be called.
As far raising exceptions look into the inspect object and frame info. There are a ton of q and as on this site about that.
tl;dr You can't make anything private but you can make it harder to find
Upvotes: 1
Reputation: 880987
You can decorate the private methods with a call to warnings.warn
.
I'm not sure what you mean by preventing people but allowing modules to call methods. Is the distinction that one happens at an interactive prompt, but the other does not? If so, you can test if python is being run from an interactive prompt by checking the value of sys.path[0]
.
When python runs a script, sys.path[0]
equals the directory of the script. When python runs an interactive session, sys.path[0]
is set to the empty string ''
. So,
to warn people, but not scripts, you could do
import warnings
import functools
def warn_private(func):
if not sys.path[0]:
@functools.wraps(func)
def wrapper(self,*args,**kwargs):
warnings.warn('{f} is private'.format(f=func.__name__))
return func(self,*args,**kwargs)
return wrapper
else:
return func
class Foo(object):
@warn_private
def _bar(self):
pass
Upvotes: 0
Reputation: 63802
Maybe define your private methods on an internal class, and access it internally using self._private.method
:
class PublicObject(object):
class PrivateObject(object):
def __init__(self, public):
self.public = public
def private1(self):
print "a private method, not visible outside the public wrapper on", id(self.public)
def __init__(self):
self._private = self.PrivateObject(self)
def public1(self):
print "a public method, which invokes a private one"
return self._private.private1()
a = PublicObject()
print dir(a) # only shows public1, not private1; won't show private1 in most IDEs autocomplete
a.public1()
a.private1() # raises an exception
a._private.private1() # still possible to call private methods, but obvious when you are doing so
If you wanted ProtectedObject with visibility up the inheritance chain, create a similar _protected
attribute and use __getattr__
to access attributes and methods on super(PublicObject,self.public)._protected
.
By the way, what you are asking is really outside the Python philosophy, and adding this indirection through a composite object will incur a performance cost.
Upvotes: 0
Reputation: 602745
Python has the philosophy of "consenting adults": Prefix methods with an underscore to mark them as private. Don't call any methods with a leading underscore from the outside. If you do, you're on your own. You are free to do so, but you have been warned.
To adopt this convetion in an existing code base, rename the original methods to names with a leading underscore, and add a wrapper with the original name that throws a warning.
Your IDE should have configurable auto-completion. If not, use Emacs :)
Upvotes: 7