159

I am coding a small Python module composed of two parts:

  • some functions defining a public interface,
  • an implementation class used by the above functions, but which is not meaningful outside the module.

At first, I decided to "hide" this implementation class by defining it inside the function using it, but this hampers readability and cannot be used if multiple functions reuse the same class.

So, in addition to comments and docstrings, is there a mechanism to mark a class as "private" or "internal"? I am aware of the underscore mechanism, but as I understand it it only applies to variables, function and methods name.

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
oparisy
  • 2,056
  • 2
  • 16
  • 17

9 Answers9

256

Use a single underscore prefix:

class _Internal:
    ...

This is the official Python convention for 'internal' symbols; "from module import *" does not import underscore-prefixed objects.

Reference to the single underscore convention.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ferdinand Beyer
  • 64,979
  • 15
  • 154
  • 145
  • 7
    I did not know the underscore rule extended to classes. I do not want to clutter my namespace when importing, so this behavior is what I was looking for. Thanks! – oparisy Feb 15 '09 at 19:52
  • 1
    Since you state that this is the "official" python convention, it would be nice with a link. Another post here says that __all__ is the official way and does link to the documentation. – flodin Feb 15 '09 at 20:00
  • 16
    http://www.python.org/dev/peps/pep-0008/ -- _single_leading_underscore: weak "internal use" indicator. E.g. "from M import *" does not import objects whose name starts with an underscore. -- Classes for internal use have a leading underscore – Miles Feb 15 '09 at 20:16
  • 3
    A leading underscore is the convention for marking things as internal, "don't mess with this," whereas __all__ is more for modules designed to be used with "from M import *", without necessarily implying that the module users shouldn't touch that class. – Miles Feb 15 '09 at 20:20
  • This does not work as expected, class will still be importable – Joabe Lucena Nov 26 '21 at 01:24
  • @JoabeLucena - Please read the question again. This is not about preventing from import, this is about marking things as private/internal. – Ferdinand Beyer Dec 01 '21 at 11:28
103

In short:

  1. You cannot enforce privacy. There are no private classes/methods/functions in Python. At least, not strict privacy as in other languages, such as Java.

  2. You can only indicate/suggest privacy. This follows a convention. The Python convention for marking a class/function/method as private is to preface it with an _ (underscore). For example, def _myfunc() or class _MyClass:. You can also create pseudo-privacy by prefacing the method with two underscores (for example, __foo). You cannot access the method directly, but you can still call it through a special prefix using the classname (for example, _classname__foo). So the best you can do is indicate/suggest privacy, not enforce it.

Python is like Perl in this respect. To paraphrase a famous line about privacy from the Perl book, the philosophy is that you should stay out of the living room because you weren't invited, not because it is defended with a shotgun.

For more information:

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Karl Fast
  • 3,926
  • 4
  • 22
  • 8
  • 5
    In response to #1, you sort of can enforce privacy of methods. Using a double underscore like __method(self) will make it inaccessible outside of the class. But there is a way around it by calling it like, Foo()._Foo__method(). I guess it just renames it to something more esoteric. – Evan Fosmark Feb 15 '09 at 20:54
46

Define __all__, a list of names that you want to be exported (see documentation).

__all__ = ['public_class'] # don't add here the 'implementation_class'
UncleZeiv
  • 18,272
  • 7
  • 49
  • 77
  • 5
    This only affects `from module import *`. You can still run `from module import privatename` as before. – Flimm Mar 28 '22 at 08:46
19

A pattern that I sometimes use is this:

Define a class:

class x(object):
    def doThis(self):
        ...
    def doThat(self):
        ...

Create an instance of the class, overwriting the class name:

x = x()

Define symbols that expose the functionality:

doThis = x.doThis
doThat = x.doThat

Delete the instance itself:

del x

Now you have a module that only exposes your public functions.

theller
  • 2,809
  • 19
  • 19
  • 6
    Took a minute to understand the purpose/function of "overwriting the class name", but I got a big smile when I did. Not sure when I'll use it. :) – Zach Young May 23 '15 at 18:39
  • 2
    Is there a name for this technique? – Bishwas Mishra Aug 04 '20 at 14:16
  • I do not understand this. Can someone please provide full code for me to try out? – usustarr Feb 19 '21 at 18:04
  • 4
    omg, this is such a hack. please never do this. cool tho – John Henckel Nov 17 '21 at 14:32
  • 1
    @JohnHenckel Why do you think this never should be done? An explanation would be nice. – HelloGoodbye Aug 05 '22 at 10:27
  • 1
    @HelloGoodbye it's not recommended because it will be very hard for the next programmer to figure out what is going on. "So... you first mask the name of the class by assigning an instance of the class to a variable of the same name, then you use that variable to expose two methods of the (now invisible) class, and then you delete the variable, and the only thing that remains is the two methods?" seems like a very subtle (and clever!) hack. – John Henckel Aug 27 '22 at 20:36
  • @BishwasMishra I suggest the name should be "Scorched Earth Technique". – John Henckel Aug 27 '22 at 20:37
  • @JohnHenckel I understand, and I agree that it confuscates things! – HelloGoodbye Aug 28 '22 at 21:53
13

The convention is prepend "_" to internal classes, functions, and variables.

Benjamin Peterson
  • 19,297
  • 6
  • 32
  • 39
7

To address the issue of design conventions, and as chroder said, there's really no such thing as "private" in Python. This may sound twisted for someone coming from C/C++ background (like me a while back), but eventually, you'll probably realize following conventions is plenty enough.

Seeing something having an underscore in front should be a good enough hint not to use it directly. If you're concerned with cluttering help(MyClass) output (which is what everyone looks at when searching on how to use a class), the underscored attributes/classes are not included there, so you'll end up just having your "public" interface described.

Plus, having everything public has its own awesome perks, like for instance, you can unit test pretty much anything from outside (which you can't really do with C/C++ private constructs).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dimitri Tcaciuc
  • 5,053
  • 5
  • 20
  • 22
  • Certainly not everyone looks at `help(MyClass)` when searching on how to use a class; I have personally never used that before. – HelloGoodbye Aug 05 '22 at 10:30
6

Use two underscores to prefix names of "private" identifiers. For classes in a module, use a single leading underscore and they will not be imported using "from module import *".

class _MyInternalClass:
    def __my_private_method:
        pass

(There is no such thing as true "private" in Python. For example, Python just automatically mangles the names of class members with double underscores to be __clssname_mymember. So really, if you know the mangled name you can use the "private" entity anyway. See here. And of course you can choose to manually import "internal" classes if you wanted to).

chroder
  • 4,393
  • 2
  • 27
  • 42
0

In fact you can achieve something similar to private members by taking advantage of scoping. We can create a module-level class that creates new locally-scoped variables during creation of the class, then use those variables elsewhere in that class.

class Foo:
    def __new__(cls: "type[Foo]", i: int, o: object) -> "Foo":
        _some_private_int: int = i
        _some_private_obj: object = o
        foo = super().__new__(cls)
        def show_vars() -> None:
            print(_some_private_int)
            print(_some_private_obj)
        foo.show_vars = show_vars
        return foo

    def show_vars(self: "Foo") -> None:
        pass

We can then do, e.g.

foo = Foo(10, {"a":1})
foo.show_vars()
# 10
# {'a': 1}

Alternatively, here's a poor example that creates a class in a module that has access to variables scoped to the function in which the class is created. Do note that this state is shared between all instances (so be wary of this specific example). I'm sure there's a way to avoid this, but I'll leave that as an exercise for someone else.

def _foo_create():
    _some_private_int: int
    _some_private_obj: object
    class Foo:
        def __init__(self, i: int, o: object) -> None:
            nonlocal _some_private_int
            nonlocal _some_private_obj
            _some_private_int = i
            _some_private_obj = o

        def show_vars(self):
            print(_some_private_int)
            print(_some_private_obj)

    import sys
    sys.modules[__name__].Foo = Foo
_foo_create()

As far as I am aware, there is not a way to gain access to these locally-scoped variables, though I'd be interested to know otherwise, if it is possible.

aholmes
  • 458
  • 9
  • 20
-2

I'm new to Python but as I understand it, Python isn't like Java. Here's how it happens in Python:

class Student:

    __schoolName = 'XYZ School'      # private attribute
    def __nameprivamethod(self):     # private function 
         print('two underscore')

class Student:

    _schoolName = 'XYZ School'       # protected attribute

Don't to check how to access the private and protected parts.

halfer
  • 19,824
  • 17
  • 99
  • 186
  • As other answers note, Python does not enforce privacy or protection; the weak notion of privacy is provided with a single leading underscore. Double-leading-underscore (or "dunder" naming invokes class-level name mangling; see [PEP8](https://peps.python.org/pep-0008/#descriptive-naming-styles) for more info. As is, this answer is misleading; "private" attrs/fns (or more aptly "hidden") are single-leading-underscore, and name-mangled (not really protected) are double-leading-underscore.. – Joshua Voskamp Jan 09 '23 at 21:00
  • how is it misleading? it do not show error when you try them. they worked fine for me . you can write an example, if you want to add an info, it's very helpful when there is a tiny easy example – MAHDI NOUIRA Jan 11 '23 at 19:27
  • 1
    I see my mistake; due to improper formatting, I did not understand that the `__schoolName` attr, for example, is intended to be *inside* the `Student` class. Then, as other answers (see e.g. [chroder's answer](https://stackoverflow.com/a/551169/4871001)) have stated, for the `Student` class to be internal, it should be called `_Student`. With that change made, then this answer is correct (though a duplicate). – Joshua Voskamp Jan 11 '23 at 20:50