Verifiably better, validated Enum for Python

Chris Angelico rosuav at gmail.com
Wed May 24 15:41:23 EDT 2017


On Thu, May 25, 2017 at 12:31 AM, Steve D'Aprano
<steve+python at pearwood.info> wrote:
> On Wed, 24 May 2017 10:07 pm, Chris Angelico wrote:
>
>> I'm sure it could be done. But would it really benefit anyone
>> anything? Java has "final" classes, which can't be subclassed; and I
>> haven't heard many people saying "I wish you would declare more of
>> your classes final" or "I wish Python let you declare a class as
>> final".
>
> I've wanted that from time to time. Never quite enough to make my own
> metaclass to get it, or enough to write a C extension, but if the
> functionality existed, I would have used it.

>From what I've heard (albeit nothing more than anecdotal), the
frustration comes from Fred marking his class final because he thinks
nobody will need to subclass it, and then Joe coming along and wanting
to subclass Fred's class. So Fred might still think "final is great,
my stuff runs 0.1% faster", but Joe thinks "final is a pain in the
behind".

> Same as if Python supported constants (names that can only be assigned to
> once). Again, the pain of building a custom "constant-like" solution is
> greater than the benefit, so I've done without.
>
> But if Python did support these things, maybe the compiler could make a few
> optimizations, or maybe we could write a few less unit tests, or catch a
> few bugs a little earlier... or not.

Named constants would definitely be a possibility for the language, as
they'd be an optimization only. (And I'm sure macropy can do it,
although I haven't checked.) The benefit would be that you don't need
to do a LOAD_GLOBAL to fetch them, plus there's the possibility of
constant-folding (eg "HOURS_PER_DAY * MINUTES_PER_HOUR *
SECONDS_PER_MINUTE" could be folded down to just 86400, instead of
three loads and two binary multiplies). In theory, the benefit of a
final class is that the compiler can statically resolve method
lookups, but I don't think you can do that in Python anyway. Consider:

@final
class Ham:
    def spam(self):
        pass
    def eggs(self):
        return self.spam()
bacon = Ham()
bacon.eggs()

Before calling Ham.spam(), Python needs to check to see if bacon
itself has an attribute spam. So there's the same risk of annoying
people that Java has, plus it's not really going to benefit you all
that much unless you also prevent instance overrides.

Now, maybe that would be a valuable thing despite the semantic change.
I don't know. But I'm not going to push for it.

> It's just a different way at looking at minimalism. Guido says, the language
> should be as small as possible. If you don't want to assign to a name, then
> just don't assign to it. No need to build support for constants into the
> language when you can trust the developer to Don't Do That.
>
> (But note that Python does support read-only attributes, a related concept.)

AIUI, read-only attributes are less about "you're not allowed to
change this" and more about "changing this doesn't even make sense".

class ChunkedBuffer:
    @property
    def capacity(self):
        return sum(chunk.capacity for chunk in self.chunks)

You can't just arbitrarily edit the capacity; which one would it
change? As a side effect, you CAN use this to enforce immutability,
but that isn't the point.

And hey. The lack of enforced read-only stuff means we don't get
people saying "look, I can change it using ctypes" :)

> While other language designers take on minimalism is that the entity in
> question should support as few things as possible. If you have a name that
> you don't want to ever be rebound to a different value, then the language
> shouldn't support rebinding that name, since you can't trust the developer
> to Don't Do That.
>
> Neither view is entirely right or wrong, which is why there will always be
> arguments over it. But I'll note that Python has supported read-only
> properties for, oh, a decade or more, and do I hear people complaining
> about how much libraries and applications over-use and abuse properties?
> Not very often.

No, and that's largely because Python has built a culture of "don't
use properties at all". The normal idiom in C++ is something like
this:

class Spam {
private:
    int _x;
public:
    Spam(int x) {_x = x;}
    int get_x() {return _x;}
    void set_x(int x) {_x = x;}
};

The normal idiom in Python is:

class Spam:
    def __init__(self, x):
        self.x = x

Look ma, no properties! So there's no abuse of properties either. But
in codebases that _do_ use @property heavily, yes, there definitely
are complaints about the over-use and abuse. Of course, that doesn't
mean that Python's way is inherently better; I'm sure that large C++
codebases have huge complaints about the over-use and abuse of public
data attributes instead of proper getters and setters. And that's fine
- for C++. Different language, different culture, different style.
Non-subclassable classes would be a perfectly acceptable thing in C++,
except for the fact that you don't need to, because you can get the
same benefits by just not declaring your methods "virtual". (Python's
inheritance model is broadly equivalent to a C++ class where
everything is virtual.)

Hmm. Actually, that might be the better way to do it, even in Python.
Instead of declaring that the class isn't subclassable, declare that
the function isn't overridable. It'd need some metaclass or class
decorator support, but it could be done: any method tagged @final, if
called from another method, will be called directly, rather than going
through the usual MRO lookups. Still not sure it'd be worth doing, but
it's something to explore, I guess.

ChrisA



More information about the Python-list mailing list