Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Counterpoint: Named tuples are immutable, while dataclasses are mutable by default.

You can use frozen=true to "simulate" immutability, but that just overwrites the setter with a dummy implementation, something you (or your very clever coworker) can circumvent by using object.__setattr__()

So you neither get the performance benefits nor the invariants of actual immutability.



Counter-counterpoint:

- Everything in Python is mutable, including the definitions of constants like `3` and `True`. It's much like "unsafe" in Rust; you can do stupid things, but when you see somebody reaching for `__setattr__` or `ctypes` then you know to take out your magnifying glass on the PR, find a better solution, ban them from the repo, or start searching for a new job.

- Performance-wise, named tuples are sometimes better because more work happens in C for every line of Python, not because of any magic immutability benefits. It's similar to how you should prefer comprehensions to loops (most of the time) if you're stuck with Python but performance still matters a little bit. Yes, maybe still use named tuples for performance reasons, but don't give the credit to immutability.


> something you (or your very clever coworker) can circumvent by using object.__setattr__()

This fits pretty well with a lot of other stuff in Python (e.g. there’s no real private members in classes). There’s a bunch of escape hatches that you should avoid (but that can still be useful sometimes), and those usually are pretty obvious (e.g. if you see code using object.__setattr__, something is definitely not right).

Can’t tell whether this is good design or not, but personally I like it.


Counterpoint: I've used `object.__setattr__` pretty often when setting values in the `__post_init__` of frozen dataclasses


I'd argue, that's about the only correct usage of this stuff.


Is there a difference between global setattr(object, v) and object.__setattr__(v)? I've seen setattr() in the wild all over but I've never encountered the dunder one.


Note that `object` here is not a placeholder variable but actually refers to the global object type (basically a superclass of pretty much every other type in Python). It allows you to bypass the classes’ __setattr__ and set the value regardless (the setattr() function can’t do that):

  In [1]: from dataclasses import dataclass

  In [2]: @dataclass(frozen=True)
     ...: class Foo:
     ...:     a: int
     ...:

  In [3]: foo = Foo(5)

  In [4]: foo.a = 10
  FrozenInstanceError: cannot assign to field 'a'

  In [5]: setattr(foo, "a", 10)
  FrozenInstanceError: cannot assign to field 'a'

  In [6]: object.__setattr__(foo, "a", 10)

  In [7]: foo.a
  Out[7]: 10


It's Python. You can override practically any behavior. Hell, use ctypes and mutate immutable tuples! Doing so is well-defined in the C API!

What bugs me more about frozen dataclasses is how post-init methods have to use the setattr hack.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: