2

I used to write classes with constant interface in C++ and want to ask you for advice: should I try to do it in my python programs?

Lets assume that I want to have immutable objects of class Point. Here is the C++ code:

class Point
{
public:
    Point(double x, double y) :
        x_{x},
        y_{y}
    { }

    double x() const { return x_; }
    double y() const { return x_; }

private:
    double x_;
    double y_;
};

Using objects of type Point I know that they will never change (I believe there are some hacks to do it, but it does not matter now).

In python I have several ways.

You are paranoid! Just keep it short and simple!

from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])

[+] Extremely clear code

[-] Does not solve the problem

Try to write only getters for class attributes.

class Point:
    def __init__(self, x, y):
        self._x = x
        self._y = y

    def x(self):
        return self._x

    def y(self):
        return self._y

[+] The leading underscore shows that attribute is "private"

[?] I don't think that it is a good idea to write the "imitation of C++-like code". Just because it is a different language.

[-] Attributes are still easy accessible. Should I name them with double underscore? (__x and __y)

One more way is to write decorator I found in this topic: How to create a constant in Python

def constant(f):
    def fset(self, value):
        raise SyntaxError
    def fget(self):
        return f()
    return property(fget, fset)

class Point:
    ...
    @constant
    def x(self)
        return self.__x

[+] Completely solves the problem (yes, I still can change values of x and y, but it becomes harder)

[?] Is it a python way at all?

[-] Too much code

So, my question is, what is the best practice?

Community
  • 1
  • 1
Unforgiven
  • 125
  • 6
  • 4
    Why doesn't a `namedtuple` class solve the problem? Note that double-underscore names are *not* private, merely obscured. Their goal is to create a namespace between the base class and its subclasses, **not** privacy. – Martijn Pieters Aug 04 '13 at 14:38
  • 1
    The original goal of `const` is optimization; the compiler can be sure the object is immutable, thus making certain speed shortcuts possible. Python doesn't offer such optimizations *anyway*, so why all the bother? – Martijn Pieters Aug 04 '13 at 14:41
  • 1
    @MartijnPieters The original goal of `const` in C++ might have been optimizations, but the more general concept of immutability is so useful, people readily introduce it even if it may decrease performance a bit. –  Aug 04 '13 at 14:43
  • @MartijnPieters - an additional benefit of using immutables is that you can freely share them among methods, threads, and processes, and you don't have to synchronize access to their contents. And you don't have to worry about some rogue method updating the contents that the caller or another thread is expecting to be constant. – PaulMcG Aug 04 '13 at 14:47
  • @MartijnPieters Thank you. I know what double-underscore really does) Namedtuple does not seem to be a solution because I don't think it provides any kind of immutability. Also I don't see any way to tell that I want this object to be immutable. **Upd** Oops, I was wrong. – Unforgiven Aug 04 '13 at 14:48

2 Answers2

1

I think namedtuple is just what you want:

>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(10,20)
>>> p.x
10
>>> p.y
20
>>> p.x = 100
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
PaulMcG
  • 62,419
  • 16
  • 94
  • 130
  • It was my fault: I was sure it will not throw an exception, I must have checked it. Thanks. – Unforgiven Aug 04 '13 at 14:51
  • List = mutable, tuple = immutable. This is why you can use a tuple (or namedtuple) as a dict key or set element, but you can't use a list. – PaulMcG Aug 04 '13 at 14:58
1

The first is great, if you don't need much functionality beyond what namedtuple offers (if you need a few methods, you can create a subclass).

Namedtuple does not seem to be a solution because I don't think it provides any kind of immutability.

That is as incorrect as it gets. It's completely immutable. It's more immutable than a user-defined class could ever be.

The second approach is indeed not very good style. Properties are preferred over trivial getters. The third uses properties, but it does so in a very roundabout and confusing way, the following code is equivalent and much simpler:

class Point:
    ...
    @property
    def x(self)
        return self._x

The decorator form of property has no setter and thus already throws an exception upon assignment (a more appropriate type of exception to boot). Double underscores are a bad choice here, they're for when subclassing is expected. This is the option I use myself and would recommend when namedtuple is not appropriate.