0

I've got a generic container class:

from typing import Container, Generic, TypeVar, NamedTuple
import sys


fixed_typing = (sys.version_info >= (3, 6, 2) or 
                (3, 5, 3) <= sys.version_info < (3, 6))
T = TypeVar("T")
_Interval = NamedTuple("_Interval", [("start", int),
                                     ("stop", int),
                                     ("data", T)])

class Interval(_Interval, Container, Generic[T]):

    if fixed_typing:
        __slots__ = ()

    ...

As you might've noticed there is a switch controlling the usage of __slots__ during class initialisation. Basically, I'd like to use slots whenever possible, but older versions of the typing module do not support slots in generic classes due to a bug. Is it okay to use the switch from the code design perspective? I've never seen it before, that's why I'm wondering. I find it similar to checking compiler flags using the preprocessor in C/C++, which is a common thing to do.

  • Can you explain specifically what you mean by "okay" in this context? – Robert Harvey Sep 4 '17 at 13:32
  • @RobertHarvey 1. is it Pythonic; 2. can it cause any headache in the future – Eli Korvigo Sep 4 '17 at 13:53
3

On the one hand, this both works and does what it says it does. So you're already ahead of a substantial amount of real-world code on that front.

On the other hand, are you going to add extra branches like this for every bug they fix in Python? It's probably not worth it, frankly. Most users who are running 3.5.x, for example, should have 3.5.3+, or else their distributors should have backported this and other bug fixes. If someone complains that your code does not run on 3.5.2, it is entirely reasonable to point them to the Python bug. If you do not want to field those complaints in the first place, you can add a version check at your program's entry point and fail-fast there with a sensible error message. Given that some distributors may be manually backporting fixes to old versions of Python, this may or may not be an ideal design decision, depending on your target audience and platform (for example, Windows users will typically be using a "stock" version of Python, while Linux users are more likely to get Python from their distro's package repository).

On the third hand, why do you need __slots__ in the first place? In most cases, you don't. In most of the cases where you do need it, you can't just yank it out to work around a bug. So why is it there at all?

On the fourth hand, based on the name of your class and its variables, you appear to be re-implementing range(), which already has a fast __contains__() method. What really confuses me, though, is this data variable. It looks like you are trying to bundle a range() together with some arbitrary object. If so, you probably want composition rather than inheritance. But either way, that's a poor choice of name.

  • 1. Unfortunately, enforcing a correct version is not an option (I've tried). – Eli Korvigo Sep 4 '17 at 19:41
  • @EliKorvigo: Only you can evaluate your precise situation and target audience, but I would encourage you to carefully consider the distribution of responsibility. It sounds like you are saying that 1) you are responsible for making your software work on the customer's system, but 2) you are not allowed to update Python on the customer's system, even within a virtualenv, and yet 3) the customer is not a Python 2.7 holdout. If all those are true, you have my sympathy. – Kevin Sep 4 '17 at 19:47
  • 2. I need __slots__, because of memory concerns – there are going to be hundreds of thousands of these objects (basically, I use __slots__ to compensate a little for the switch from C/Cython to pure Python, which was all for the generic type annotations); 3. __contains__ is implemented differently (it checks for data), the naming convention (including attribute names) deliberately mirrors that of Python's range. – Eli Korvigo Sep 4 '17 at 19:47
  • @EliKorvigo: There is no data attribute in a range object. – Kevin Sep 4 '17 at 19:49
  • I know. That attribute was added to store annotation (this objects are supposed to live in an AVL-tree, representing annotation). – Eli Korvigo Sep 4 '17 at 19:51
1

It's pretty reasonable to expect people to upgrade their version of python to the latest of the same minor release. For example, telling people to upgrade from 3.5.3 to 3.5.4 or from 3.6.1 to 3.6.2. In that case I'd do something like:

assert (sys.version_info >= (3, 6, 2) or 
                (3, 5, 3) <= sys.version_info < (3, 6)), "Your version of python is buggy! Please upgrade!"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.