Why is Python sys.version_info missing _asdict() method to cast to a dict?

Viewed 422

Is there something unique about sys.version_info that means it doesn't return a proper namedtuple and doesn't have the _asdict function?

sss = sys.version_info._asdict
AttributeError: 'sys.version_info' object has no attribute '_asdict'
[Finished in 0.7s with exit code 1]
2 Answers

version_info isn't ~quite a namedtuple (despite the docstring).

(below assumes cpython implementation detail, it may or may not apply to alternate implementations such as pypy / jython)

It is implemented in C, a StructSequence. From the 3.7.1 sources:

    version_info = PyStructSequence_New(&VersionInfoType);
    if (version_info == NULL) {
        return NULL;
    }

A StructSequence from the docs is:

the C equivalent of namedtuple() objects, i.e. a sequence whose items can also be accessed through attributes. To create a struct sequence, you first have to create a specific struct sequence type.

That is, it's like a namedtuple, but not the same. Notably it appears to be missing the _replace, _asdict, _fields, and _fields_defaults apis.

Interesting so I actually dug deeper. As mentioned in the comments, sys.version_info is a bespoke tuple subclass and not to be confused by the docstring that interestingly actually says its a named tuple, though they probably were referring to the print string format.

print(sys.version_info.__doc__)
sys.version_info

Version information as a named tuple.

You will also realize that if you run dir(sys.version_info) which returns its methods, _asdict or dict is not part of it and therefore returned your error where it does no have _asdict as an attribute.

According to the documentation itself;

A tuple containing the five components of the version number: major, minor, micro, releaselevel, and serial. All values except releaselevel are integers; the release level is 'alpha', 'beta', 'candidate', or 'final'. The version_info value corresponding to the Python version 2.0 is (2, 0, 0, 'final', 0). The components can also be accessed by name, so sys.version_info[0] is equivalent to sys.version_info.major and so on.

Seeing that the components are static and as mentioned in the documentation, the components can always be accessed by name or their fixed indexes.

If you would really want a dictionary out of it:

comp = 'major minor micro releaselevel serial'.split()
svi_dic ={k:v for (k,v) in zip(comp,sys.version_info)}
svi_dic

{'major': 3, 'minor': 6, 'micro': 6, 'releaselevel': 'final', 'serial': 0}

It seems redundant as you can also easily do sys.version_info.major and so on. Hope this helps gives you an understanding though.

Related