3 ms·
I mostly migrated to frozen dataclasses from namedtuples when dataclasses became available. I’m curious about your preference for the namedtuple. Is it the ligh
by sevensor 2y ago
I mostly migrated to frozen dataclasses from namedtuples when dataclasses became available. I’m curious about your preference for the namedtuple. Is it the lighter weight, the strong immutability, the easy destructing? Or is it that most tuples might as well be namedtuples? Those are the advantages I can think of anyway :)
- sgarland 2y agoThe main thing I find myself using them for is `_make()`. From the canonical [0] example: import sqlite3 EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade') 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) You could of course accomplish the same with a dictionary comprehension, but I find this to be less noisy. Also, they have `_asdict()` should you want to have the contents as a dict. [0]: https://docs.python.org/3/library/collections.html#collections.namedtuple https://docs.python.org/3/library/collections.html#collectio...
- gcr 2y agoHoly shit that’s really clever. Didn’t know about _make, thank you!
- sgarland 2y agoI didn’t either until I read docs tbf. It’s just kind of thrown in as an afterthought for the section, too.
- judicious 2y agoDictionary comprehensions can be very elegant. List and dictionary comprehensions are very powerful and expressive abstractions. In fact, while not good practice you can pretty much write all Python code inside comprehensions including stuff regarding mutation. This is valid(as in it will run, but highly unidiomatic) code: quicksort = lambda arr: [pivot:=arr[0], left:= [x for x in arr[1:] if x < pivot], right := [x for x in arr[1:] if x >= pivot], quicksort(left) + [pivot] + quicksort(right)][-1] if len(arr) > 1 else arr print(quicksort([1, 33, -4, -2, 110, 5, 88]))
- hansvm 2y agoSometimes mutations in comprehensions are very expressive. def scan(items, f, initial): x = initial return (x := f(x, y) for y in items) There are lots of other short ways to write `scan`, but I don't think any of them map so clearly to a naive definition of what it's supposed to do.
- judicious 2y agoThat's incredibly clever, generators are underrated. I once challenged my friend to do leetcode problems with only expressions. Here's levenshtein distance, however it's incredibly clunky. levenshtein_distance = lambda s1, s2: [matrix := [[0] * (len(s2) + 1) for _ in range(len(s1) + 1)], [ [ (matrix[i].__setitem__(j, min(matrix[i-1][j] + 1, matrix[i][j-1] + 1, matrix[i-1][j-1] + (0 if s1[i-1] == s2[j-1] else 1))), matrix[i][-1])[1] for j in range(1, len(s2) + 1) ] for i in range(1, len(s1) + 1) ], matrix[-1][-1]][-1]
- wodenokoto 2y agoI'm not quite sure how the fetchall() return type looks, but couldn't you just for name, age, title in cursor.fetchall(): print(name, age, title) Ofcourse you have to come up with different variable names, but it still seems more elegant to just unpack.
- Joker_vD 2y agoHonestly, the "proper" way should be passing something like this def namedtuple_factory(cursor, row): fields = [column[0] for column in cursor.description] cls = namedtuple("Row", fields) return cls._make(row) to the fetchall(), to automatically keep the names in sync with those in the SQL query string.
- ttyprintk 2y agoImplemented as conn.row_factory = sqlite3.Row
- sgarland 2y agoBy default, List[Tuple]]. You could do a list comp over the fetchall(), but at that point there’s already some magic happening, so why not make it explicit?
- kstrauser 2y agoYou don't need `_make()` with dataclasses, and you get `asdict()` as a stand-alone function so it doesn't clash with each class's namespace. Here's what your code might look like with them: import sqlite3 from dataclasses import asdict, dataclass @dataclass class EmployeeRecord: name: str age: int title: str department: str paygrade: str conn = sqlite3.connect("/companydata") cursor = conn.cursor() cursor.execute("SELECT name, age, title, department, paygrade FROM employees") for emp in (EmployeeRecord(*row) for row in cursor.fetchall()): print(emp.name, emp.title) print(asdict(emp))
- est 2y agoIf I find my self write a[0] a[1] a[2] in more than one place, I would upgrade it to a namedtuple. Much better readability, can be defined inline like `MyTuple = namedtuple('MyTuple', 'k1 k2 k3')`
- gcr 2y agoAny reason not to consider pydantic as the next step?
- JimDabell 2y agoEvery time I’ve used Pydantic I’ve found it to be a tonne of friction. The developer ergonomics just don’t seem right. These days I use attrs and cattrs, and I’m much happier. Everything feels a lot more straightforward. attrs is what Python’s dataclasses were based on, but they kept on improving it, so attrs just feels like standard Python with a little bit extra.