Skip to content Skip to sidebar Skip to footer

Python: Immutable Private Class Variables?

Is there any way to translate this Java code into Python? class Foo { final static private List thingies = ImmutableList.of(thing1, thing2, thing3); } e.

Solution 1:

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 (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) .

classFooMeta(type):
    """A type whose .thingies attribute can't be modified."""def__setattr__(cls, name, value):
        if name == "thingies":
            raise AttributeError("Cannot modify .thingies")
        else:
            returntype.__setattr__(cls, name, value)

    def__delattr__(cls, name):
        if name == "thingies":
            raise AttributeError("Cannot delete .thingies")
        else:
            returntype.__delattr__(cls, name)

thing1, thing2, thing3 = range(3)

classFoo(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 AttributeErrordel Foo.thingies # raise an AttributeError
Foo.other = Foo.other + [4] # no exceptionprint 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.

Solution 2:

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.

Solution 3:

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.

classFoo(object):    # don't need 'object' in Python 3    @propertydefthingies(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.

classFooMutable(object):
    _thingies = [1, 2, 3]
    @propertydefthingies(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:

classFooMutable(object):
    @propertydefthingies(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.

Solution 4:

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:

classmyClass(object):
  _x = "Hard Coded Value"defset_x(self, val): returndefget_x(self): returnself._x

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

Solution 5:

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

from typing importListfrom typing_extensions import Final

classFoo:
    __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

Post a Comment for "Python: Immutable Private Class Variables?"