开发者

python: immutable private class variables?

开发者 https://www.devze.com 2023-04-04 20:06 出处:网络
Is there any way to translate this Java code into Python? class Foo { final static private List<Thingy> thingies =

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 d开发者_如何学Goefine static class variables from this question Static class variables in Python but I don't know how to make them immutable and private.


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.


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.


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.


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.


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

0

精彩评论

暂无评论...
验证码 换一张
取 消