Regular lists seem to work perfectly for this
I find Pythons core data structures really convenient and easy to use, but sometimes the C++ programmer in me wishes that I had more options available to me (eg, Python has lists and tuples, C++ has double ended linked lists, arrays, tuples, vectors, double ended vectors in the standard library), the idea being that I can choose something that is a very close match to usage patterns.
Having said that, I’ve never actually found it to be a limitation in real Python code and when memory or performance matters enough that these differences start to show, Python is usually no longer the best tool for the job anyway, so it doesn’t really need these things. Still, sometimes I feel like I want them anyway, probably just because I’ve done enough C++ to be used to having them :)
Of course, an argument could be made that looking just at the language (without third party libraries) is unrealistic and useless, and its true. You could also argue that Python libraries are easier to install and use than C++ libraries (which is also true), but when you look at the languages in isolation, Python only has a few basic structures and C++ has multiple ones.
Again, I acknowledge that this is an academic discussion since in real life, I’ve never actually seen it matter and, when it does, libraries like numpy that delegate the work to C exist and are easy to use.
Also, having these specific structures as part of the standard library would make you think that what you're doing is acceptable: thinking about the overhead of a doubly-linked-list when your variable assignment operation requires an access to a Namespace object based on a variation of a HashMap.
Seriously: whenever you need a fast solution, Python doesn't stop you from building something in C++. But trying to make Python into C++ is either an incredibly long shot or just having no idea what Python is actually useful for.
E.g. points on a plane (x,y) and an algorithm that sums them to find some center etc.
In [1]: class A:
...: __slots__ = ('a', 'b')
...: def __init__(self):
...: self.a = 1
...: self.b = 2
...:
In [3]: get_size(A())
Out[3]: 112
In [4]: get_size((1,1))
Out[4]: 120
Smaller than a tuple. Not to say that 112 bytes is great, but __slots__ can really help with memory usage. Without: In [23]: get_size(A())
Out[23]: 324
A named tuple, while not mutable, is 92 bytes with those two attributes.A type-homogeneous, variable length sequence (List[int]) should be a list/generator/etc. or in some cases a tuple (for example if it was declared explicitly). Other great places for a tuple: the default argument for something that takes in a Sequence, since defaulting to an empty list can be dangerous.
However, for immutable, fixed-length, type-heterogenous sequences (which is where tuples are often used, implicitly as return types etc), a namedtuple (python's lightweight record type) is almost always superior (the only question is "is it worth it to define this"), and for mutable, fixed-length, type-heterogenous sequences (ie. points you can modify), dataclasses/attrs (python3.7 standard library dataclasses, attrs in python2.7+) are almost certainly preferable.
Basically, when you have a known number of attributes, you can give them names better than `[0]` and `[1]`.
And the big problem with namedtuples is that definition is so heavyweight - requires a new line of code, a new class object, picking the scope for that class (top-level means you have to keep track of your nonlocal definition), and namedtuples often block pickling.
I use argparse.Namespace but it has some memory issues namedtuple was invented to solve.
You can use a pandas series which has advantages, esp with many similar objs, but brings a new dependency and has some of the definitional problems.
Python is crying out for a lightweight namedtuple for this purpose.
But for performance reasons you'd probably want to use a numpy array to store your points if you're going to be doing a lot of operations over them.