9 ms·
It seems like the python maintainers always try to complicate things for themselves to cater for some obscure use case that nobody use. I mean really, when was
by Too 9y ago
It seems like the python maintainers always try to complicate things for themselves to cater for some obscure use case that nobody use. I mean really, when was the last time you ever accessed a namedtuple by index?? (For vectors i do see a benefit that you can throw it right into a matrix multiplication that is designed for arrays but really, you can do that equally easy in other ways). And constructing them on the fly doesn't make much sense either as it defats the benefits of a shared type across the code base.
All people ever wanted was just a way to write in one line ´Point3D = struct(x, y, z)´ that translates into the exact equivalent of:
class Point3D:
def __init__(x, y, z):
self.x = x
self.y = y
self.z = z
What would be the startup performance of parsing the above 5 line class compared to a namedtuple? Surely it should be faster to create it with a shorthand form built into the language, if the functionality is equivalent?
- zodiac 9y ago> What would be the startup performance of parsing the above 5 line class compared to a namedtuple? I think https://news.ycombinator.com/item?id=15135147 https://news.ycombinator.com/item?id=15135147 explains why you can't optimize namedtuple like that, since dynamic dispatch makes calls to them not "the exact equivalent" of that code. The proposal to move them to a top level data structure could solve this problem though, I think.
- joshuamorton 9y ago>It seems like the python maintainers always try to complicate things for themselves to cater for some obscure use case that nobody use. I mean really, when was the last time you ever accessed a namedtuple by index?? I do things like for x, y in iterable_of_ntuples: #process all the time
- Grue3 9y agoThat can be solved by implementing __iter__ method on the class, isn't it?
- wiredfool 9y agoNo, it's an iterable containing named tuples, not iterating over a named tuple.
- infogulch 9y agoI suppose if python had object destructuring like newish javascript (I think that's what it's called) it would be trivial. I don't know what you'd do instead in this case that doesn't involve adding additional methods or something.
- devnonymous 9y agoObject destructuring is tuple unpacking in python which is exactly what the code example does. What the parent was remarking on was caring about the performance of that rather than the ability.
- the_mitsuhiko 9y agoExcept in javascript you can unpack by name. This would solve this issue as well.
- filmor 9y agoThat's not the point, destructuring works fine on classes implementing `__iter__`: class A: def __iter__(self): return iter(range(3)) for x, y, z in [A(), A(), A()]: print(x, y, z) Prints 0 1 2 0 1 2 0 1 2
- carapace 9y agoTIL, cheers!
- scbrg 9y agoIt's both. The unpacking of the individual tuples are managed through the iterator protocol as well.
- yes_or_gnome 9y agoYour Point3D class is _not_ functionally equivalent to a NamedTuple. The class is so minimal that I wouldn't bother calling it a class as much as I would call it a glorified dictionary. I do like the concept of Structs. I first used them in Ruby, so I figured that all "very high" languages had the concept. Python, however "struct" has a very different meaning in Python. sigh It's intended for encoding/decoding binary data from strings/bytes. https://docs.python.org/3.6/library/struct.html https://docs.python.org/3.6/library/struct.html There is a lesser known use case for the type(...) function. Normally, one would use it to find out an object's type. >>> type(42) <type 'int'> >>> type("foo") <type 'str'> >>> type(type) <type 'type'> But, it can also be used to create new class types. https://docs.python.org/3.6/library/functions.html#type https://docs.python.org/3.6/library/functions.html#type >>> Point3D = type("Point3D", (object,), dict(x=0,y=0,z=0)) >>> my_point = Point3D() >>> my_point.x 0 >>> my_point.y = 42 >>> my_point.y 42 >>> my_point.a Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Point3D' object has no attribute 'a' Which is, essentially, what Ruby's Struct class provides. And, Python objects don't have privacy, so it's more comparable to the OpenStruct class. Edit: Just wanted to tack on a shout out to Attrs which is really nice improvement to "Struct" concept. http://www.attrs.org/en/stable/ http://www.attrs.org/en/stable/ h/t hynek
- joshuamorton 9y ago>Python, however "struct" has a very different meaning in Python. sigh It's intended for encoding/decoding binary data from strings/bytes. That's because the struct module is for dealing with structs, in the C/C++ sense. Like actual structs. That aside, attrs is a great library!
- vanni 9y agoA good link about metaclasses in Python: https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python https://stackoverflow.com/questions/100003/what-is-a-metacla...
- gvx 9y agoNamedtuple is used a lot in the standard library to return struct-like results which used to be returned as an actual tuple, so making them non-indexable would break a lot of code, some of which was written before namedtuple even existed.
- eesmith 9y agoI did that analysis a day ago when this topic came up previously, at https://news.ycombinator.com/item?id=15136961 https://news.ycombinator.com/item?id=15136961 . There are 13 locations in the standard library where a namedtuple was used to replace a tuple, where making them non-indexable would break code. There are 8 locations where it's used as a shorter way to write a simple class. My point in that thread was that namedtuple should never be used this way.
- gvx 9y agoI think namedtuple is a terrible hack that never should have been (at least not this way), but changing it is going to be a PITA.
- deleted 9y ago[deleted]
- goldenkey 9y agoWhats beautiful about JS is that an Array can also be used as an object. ie...namedtuple in JS var nt = [a, b, c] [nt.a, nt.b, nt.c] = nt nt.freeze() // now immutable
- joejev 9y agoOne advantage of namedtuple is that it is just a tuple! You can pass the namedtuple to code that expects a tuple, even C code that expects a tuple. This makes it easy to add names to tuples after the fact without breaking existing code. In one project, I use a Point2D class as a namedtuple. Because it is just a tuple, I can easy convert it to a numpy array when working with a collection of points.
- jquast 9y agoYour version is mutable, whereas a namedtuple is immutable. a very important difference for my field. I use namedtuples all the time. Also, I can't expand an instance of your Point3d as arguments to a function, DrawPoint(*p), I would have to write DrawPoint(p.x, p.y, p.z), did I preserve order correctly? Whereas a namedtuple expands and preserves order.
- IgorPartola 9y agoWouldn't it be so convenient if Python provided a way to define immutable classes? Also, you can always provide a `draw_point(*p.args)` which isn't bad at all.
- yes_or_gnome 9y agoYou can get close. Set __slots__ and then define each as a property with no setter and no deleter. However, an object can still be modified if it stores a mutable object (list, dict) and, while I'm not in front of a computer atm,I believe you can overwrite __slots__ to do whatever. Here's an SO answer that explains more than I'll ever know about slots. https://stackoverflow.com/questions/472000/usage-of-slots https://stackoverflow.com/questions/472000/usage-of-slots
- IgorPartola 9y agoRe: storing mutable objects, yea but tuples do that too: t = ({}, 1) t[0]['foo'] = 'bar'
- jdboyd 9y agoWhile I don't explicitly use index access to NamedTuples, I do make use of NamedTuples being a subclass of tuple, and I know I'm not the only one.
- znpy 9y agoThe main issue I see is caring too much about other implementation of Python. While that is a very kind behavior, it brings more complexity to the issue. The CPython developers should, imho, define/consolidate the API and then choose the implementation that yields the best performance for their implementation. Developers of other Python implementation will surely find a way to implement that API with optimal performances with regards to the peculiarities of their own implementations.
- coldtea 9y ago>It seems like the python maintainers always try to complicate things for themselves to cater for some obscure use case that nobody use. I mean really, when was the last time you ever accessed a namedtuple by index?? Notice how when someone asks something like that, some bizarro practitioners of such methods would always appear and say how they are invaluable for them...
- orf 9y agoIt's invaluable in several use cases, especially when working with external code that expects a tuple, when unpacking a tuple (a, b = c) especially when iterating over them in a loop (for a, b in c). You have used some of these methods before. So are you bizzaro? No. Nobody is bizarro for mentioning this, it's extremely handy and it's kind of in the name: namedtuple. Without this they are utterly pointless.
- adambyrtek 9y agoIt's called a tuple so why would accessing its elements by index be "bizarre" in any way?
- emmelaich 9y agoBecause order should be unimportant for a tuple.
- danpalmer 9y ago> I mean really, when was the last time you ever accessed a namedtuple by index? Yesterday. I use this quite a bit. I like non-named tuples as a data structure and use them regularly, and often partially upgrade a tuple to a named tuple. Changing these sorts of things is what caused the Python 2/3 split, I don't think we as a community want that again for a while, so backwards compatibility for things like this that aren't _that_ uncommon should be a priority.
- pmiller2 9y ago"Partially upgrade a tuple"? What does that mean?
- 4c2383f5c88e911 9y agoIt means that if you have a data structure that is represented by a tuple, you can switch the creation call with a namedtuple of the same size, keeping the tuple API, therefore not breaking any older code that may rely on it, while at the same time providing a cleaner interface (using the attribute names) to newer code. Of course, old code should be upgraded to use attribute names, but it's really convenient to be able to upgrade it painlessy.
- eesmith 9y agoAs a concrete example, in Python 1.5.2, urlparse.py:urlparse returned a 6-element tuple: # Parse a URL into 6 components: # <scheme>://<netloc>/<path>;<params>?<query>#<fragment> # Return a 6-tuple: (scheme, netloc, path, params, query, fragment). # Note that we don't break the components up in smaller bits # (e.g. netloc is a single string) and we don't expand % escapes. def urlparse(url, scheme = '', allow_fragments = 1): ... tuple = scheme, netloc, url, params, query, fragment _parse_cache[key] = tuple return tuple This API was around for years, with people using tuple indexing either explicitly or through destructuring assignment. This was replaced with a namedtuple. The following is from Python 2.7.10: class ParseResult(namedtuple('ParseResult', 'scheme netloc path params query fragment'), ResultMixin): __slots__ = () def geturl(self): return urlunparse(self) def urlparse(url, scheme='', allow_fragments=True): """Parse a URL into 6 components: <scheme>://<netloc>/<path>;<params>?<query>#<fragment> Return a 6-tuple: (scheme, netloc, path, params, query, fragment). Note that we don't break the components up in smaller bits (e.g. netloc is a single string) and we don't expand % escapes.""" tuple = urlsplit(url, scheme, allow_fragments) scheme, netloc, url, query, fragment = tuple if scheme in uses_params and ';' in url: url, params = _splitparams(url) else: params = '' return ParseResult(scheme, netloc, url, params, query, fragment) The old tuple-based API still works, so it doesn't break backwards compatibility, while it also gives people the ability to migrate to an attribute-based API. That said, it's very hard to remove the old API. The latest version of Python, in url/parse.py:urlparse, still supports tuple-based indexing. Perhaps Python 4 will remove that behavior? Probably not. At the very least there would need to be a namedtuplemigration type which gives a warning if the old tuple API is used when only attribute lookup is expected.
- nine_k 9y agoA tuple is immutable, your `Point3D` is not. It's a huge difference.
- Redoubts 9y ago> when was the last time you ever accessed a namedtuple by index If you don't want that, why are you using "Named[ThingWhichWasOnlyAccessibleByIndex]" Use this instead, or something class Struct(object): def __init__(self, **kwargs): self.__dict__.update(kwargs)
- stubish 9y ago> I mean really, when was the last time you ever accessed a namedtuple by index? They are good for database stuff. The row gets stuffed into the tuple, by index, and then you get to write readable code accessing the fields by name.
- raymondh 9y ago> All people ever wanted was just a way to write in one line ´Point3D = struct(x, y, z)´ that translates into the exact equivalent of [... a regular class with just an x, y, and z attributes]. You're giving up a lot: * The namedtuple is dramatically more space efficient (which will be important if you have a lot of points). * The namedtuple has a nice self-describing __repr__() (which will can important when you're debugging): >>> focal_point Point3D(x=10.2, y=8.5, z=13.1) * The namedtuple defines an equality relation (which is important if you ever want to recognize that two points are the same or need to find-out whether a given point is in a container): >>> new_point in visited_points True * The namedtuple is hashable (this will be important if want to pass points into a function that uses functools.lru_cache() for example). * The namedtuple is unpackable. This will important with loops like such as: for x, y, z in points: print(x*y - y*z) * The namedtuple works with just about every existing tool in the language that expects regular tuples (such as old-style string formatting). >>> 'Horizontal: %f Vertical: %f Depth: %f' % focal_point 'Horizontal: 10.200000 Vertical: 8.500000 Depth: 13.100000' * The namedtuple has lexicographic ordering (which may be nice if you want to pprint() a set of points in a deterministic order). * The nametuple has a _make() method for casting existing tuples without unpacking them . This is useful with the sqlite3 and csv modules: EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade') import csv for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))): print(emp.name, emp.title) import sqlite3 conn = sqlite3.connect('/companydata') cursor = conn.cursor() cursor.execute('SELECT name, age, title, department, paygrade FROM employees') for emp in map(EmployeeRecord._make, cursor.fetchall()): print(emp.name, emp.title) * The namedtuple is sliceable (useful for computing projections or partials to 2D points): >>> focal_point[:2] (10.2, 8.5) * Many other little things as well: conversion to a dictionary with _asdict(), cloning with "newpoint = oldpoint._replace(y=0, z=1)", etc.
- Too 9y agoMaybe exact equivalent was an exaggeration, but anyway.. Half of those examples are just valid because x,y,z in particular have a de-facto ordering that everybody follows, for any other record type it's just confusing (what does slicing an EmployeeRecord mean??!). Csv module will produce named records based on the header so i don't see the use there. With sqllite3 you can set row_factory=Row which will produce results which already has access by name. If you for some reason need to convert to a dictionary, you could always use __dict__ Comparability, space, hashing and immutability are important though. Altough frankly, i can't take any immutability argument seriously since python has refused to add a constants for decades, it's just hypocrisy to one day have the philosophy that everything is dynamic and then then the next day say it's not.
- alexchamberlain 9y agoCheckout [attrs](http://attrs.readthedocs.io/en/stable/ http://attrs.readthedocs.io/en/stable/) for a very powerful, yet equivalently short way of defining classes. Personally, I prefer defining them with the decorator rather than the shorthand, but attrs is definitely my library of choice for value semantic types.