17

Is there any way to translate this Java code into Python?

class Foo
{
    final static private List<Thingy> thingies = 
       ImmutableList.of(thing1, thing2, thing3);
}

e.g. thingies is an immutable private list of Thingy objects that belongs to the Foo class rather than its instance.

I know how to define static class variables from this question Static class variables in Python but I don't know how to make them immutable and private.

Community
  • 1
  • 1
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • 3
    There is no built-in concept of private variables like you have in Java (that isn't pythonic) there are only conventions people tend to obey (but it's still not the same): http://docs.python.org/tutorial/classes.html#private-variables – wkl Sep 13 '11 at 18:56
  • 3
    Python quote. *We are all gentlemen here right?* – Jakob Bowyer Sep 13 '11 at 19:10
  • 1
    I think it's "we're all adults here." But the point is very, very important. The source is visible. Fussing around about "privacy" isn't very helpful. – S.Lott Sep 13 '11 at 19:33
  • 3
    Python is not Java: http://dirtsimple.org/2004/12/python-is-not-java.html – Wilduck Sep 13 '11 at 19:34
  • @JakobBowyer no, some people here will be women – joel Jun 21 '20 at 15:36
  • @Wilduck "Python is not Java" gives the impression that nothing in Java should be done in Python, which I don't think anyone would argue. That is, it's misleading – joel Jun 21 '20 at 15:51
  • Absence of defined constants in Python is a feature. For example if you only want approximate answers in a numerical calculation, you are free to write math.pi = 3. – zizzler Nov 08 '20 at 15:30

5 Answers5

33

In Python the convention is to use a _ prefix on attribute names to mean protected and a __ prefix to mean private. This isn't enforced by the language; programmers are expected to know not to write code that relies on data that isn't public.

If you really wanted to enforce immutability, you could use a metaclass[docs] (the class of a class). Just modify __setattr__ and __delattr__ to raise exceptions when someone attempts to modify it, and make it a tuple (an immutable list) [docs].

class FooMeta(type):
    """A type whose .thingies attribute can't be modified."""

    def __setattr__(cls, name, value):
        if name == "thingies":
            raise AttributeError("Cannot modify .thingies")
        else:
            return type.__setattr__(cls, name, value)

    def __delattr__(cls, name):
        if name == "thingies":
            raise AttributeError("Cannot delete .thingies")
        else:
            return type.__delattr__(cls, name)

thing1, thing2, thing3 = range(3)

class Foo(object):
    __metaclass__ = FooMeta
    thingies = (thing1, thing2, thing3)
    other = [1, 2, 3]

Examples

print Foo.thingies # prints "(0, 1, 2)"
Foo.thingies = (1, 2) # raises an AttributeError
del Foo.thingies # raise an AttributeError
Foo.other = Foo.other + [4] # no exception
print Foo.other # prints "[1, 2, 3, 4]"

It would still technically be possible to modify these by going through the class's internal .__dict__ of attributes, but this should be enough to deter most users, it's very difficult to entirely secure Python objects.

Jeremy
  • 1
  • 85
  • 340
  • 366
  • 8
    +1. I'll add a warning: Just because Python allows you to do this doesn't mean you should. Good Java is not necessarily good Python (and vice versa). – Steven Rumbalski Sep 13 '11 at 19:23
  • @StevenRumbalski: Definitely. I've added such disclaimer to my post. – Jeremy Sep 13 '11 at 19:30
  • Serious overkill when you can use properties. – Ethan Furman Sep 13 '11 at 19:42
  • 2
    @EthanFurman: The answers using `property` are protected the instance attribute `Foo().thingies`, he asked to protect the class attribute `Foo.thingies`. I could have also used properties here, but lots of people don't understand metaclasses and I wanted to have a relatively straightforward example. – Jeremy Sep 13 '11 at 19:44
  • I'm not sure this is a good example, though, as `thingies` can still be rebound on the instances. However, with properties (as you said), the class attribute of `thingies` can be rebound -- apparently the complete solution is a combination of metaclass and properties. – Ethan Furman Sep 13 '11 at 20:12
  • what is **type** over here? – user5319825 Sep 13 '16 at 11:49
  • @user5319825 Type is `type`, the base meta-class of which most classes are instances. Metaclasses are a bit complicated to clearly explain in a comment, [you can try the docs](https://docs.python.org/3/reference/datamodel.html#customizing-class-creation), but those still aren't perfect. – Jeremy Sep 13 '16 at 19:51
20

You can't do either of those things in Python, not in the sense you do them in Java, anyway.

By convention, names prefixed with an underscore are considered private and should not be accessed outside the implementation, but nothing in Python enforces this convention. It's considered more of a warning that you're messing with an implementation detail that may change without warning in a future version of the code.

kindall
  • 178,883
  • 35
  • 278
  • 309
  • 3
    Immutability can be achieved by using `property()`. Private variables could, theoretically, be achieved the same way by having the getter perform some check to determine whether or not the it was an instance, and returning some acceptable default in place of the actual value based on the check. At least, I think that'd be possible. I haven't ever found a need to do so ... – g.d.d.c Sep 13 '11 at 18:56
  • 3
    It'd be a lot of work just to get something which is nonidiomatic and seen as unnecessary. Just prefix is with `_`. –  Sep 13 '11 at 18:59
  • Properties can be made read-only only on instances, so you'd have to define them on a metaclass to get the desired behavior on the class. And you'd still be able to change them; you'd merely have to change them on the metaclass. – kindall Sep 18 '11 at 18:36
  • Properties still have to store the value somewhere. Typically, these are stored on the instance in an attribute beginning with an underscore... where they are just as unprotected as without the property. – kindall Jul 12 '23 at 15:47
9

You can make it un-writeable (subtly different from immutable) by using properties, but there is no way to make it private -- that goes against Python's philosophy.

class Foo(object):    # don't need 'object' in Python 3
    @property
    def thingies(self):
        return 'thing1', 'thing2', 'thing3'

f = Foo()
print f.thingies
#('thing1', 'thing2', 'thing3')
f.thingies = 9
#Traceback (most recent call last):
#  File "test.py", line 8, in <module>
#    f.thingies = 9
#AttributeError: can't set attribute

Whether it's immutable or not depends on what you return; if you return a mutable object you may be able to mutate that and have those changes show up in the instance/class.

class FooMutable(object):
    _thingies = [1, 2, 3]
    @property
    def thingies(self):
        return self._thingies

foo = FooMutable()
foo.thingies.append(4)
print foo.thingies
# [1, 2, 3, 4]

This will let you mutate thingies, and because the object returned is the same object kept in the instance/class the changes will be reflected on subsequent access.

Compare that with:

class FooMutable(object):
    @property
    def thingies(self):
        return [1, 2, 3]

foo = FooMutable()
foo.thingies.append(4)
print foo.thingies
# [1, 2, 3]

Because a brand new list is returned each time, changes to it are not reflected in subsequent accesses.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
2

You want to look into the property() function. It allows you to define your own custom Getter and Setter for a member attribute of a class. It might look something like this:

class myClass(object):
  _x = "Hard Coded Value"

  def set_x(self, val): return

  def get_x(self): return self._x

  def del_x(self): return

  x = property(get_x, set_x, del_x, "I'm an immutable property named 'x'")

I haven't used it enough to be certain whether it can be used to create something "private" so you'd have to delve into that yourself, but isinstance may help.

g.d.d.c
  • 46,865
  • 9
  • 101
  • 111
0

You can achieve the final part using type hints*. As others have said, __ achieves the private aspect well enough, so

from typing import List
from typing_extensions import Final

class Foo:
    __thingies: Final[List[Thingy]] = ImmutableList.of(thing1, thing2, thing3)

I'll leave the definition of ImmutableList to you. A tuple will probably do.

*with the usual caveat that users can ignore them

joel
  • 6,359
  • 2
  • 30
  • 55