title: Why is dataclasses.astuple returning a deepcopy of class attributes?
body: <p>In the code below the <code>astuple</code> function is carrying out a deep copy of a class attribute of the dataclass. Why is it not producing the same result as the function <code>my_tuple</code>? </p>
  
  <pre><code>import copy
  import dataclasses
  
  
  @dataclasses.dataclass
  class Demo:
      a_number: int
      a_bool: bool
      classy: 'YOhY'
  
      def my_tuple(self):
          return self.a_number, self.a_bool, self.classy
  
  class YOhY:
      def __repr__(self):
          return (self.__class__.__qualname__ + f" id={id(self)}")
  
  
  why = YOhY()
  print(why)  # YOhY id=4369078368
  
  demo = Demo(1, True, why)
  print(demo)  # Demo(a_number=1, a_bool=True, classy=YOhY id=4369078368)
  
  untrupled = demo.my_tuple()
  print(untrupled)  # YOhY id=4369078368
  
  trupled = dataclasses.astuple(demo)
  print(trupled)  # YOhY id=4374460064
  
  trupled2 = trupled
  print(trupled2)  # YOhY id=4374460064
  
  trupled3 = copy.copy(trupled)
  print(trupled3)  # YOhY id=4374460064
  
  trupled4 = copy.deepcopy(trupled)
  print(trupled4)  # YOhY id=4374460176
  </code></pre>
  
  <p><strong>Footnote</strong></p>
  
  <p>As <a href="https://stackoverflow.com/users/812183/anthony-sottile">Anthony Sottile's</a> excellent response makes clear this is the behavior coded into Python 3.7. Anyone expecting astuple to unpack the same way as collections.namedtuple will need to replace it with a method similar to <code>Demo.my_tuple</code>. The following code is less fragile than my_tuple because it will not need modification if the fields of the dataclass are changed. On the other hand it won't work if <code>__slots__</code> are in use.</p>
  
  <p>Both versions of the code pose a threat whenever a <code>__hash__</code> method is present in the class or its superclasses. See the Python 3.7 documentation for <code>unsafe_hash</code> in particular the two paragraphs beginning 'Here are the rules governing implicit creation of a <code>__hash__()</code> method'.</p>
  
  <pre><code>def unsafe_astuple(self):
      return tuple([self.__dict__[field.name] for field in dataclasses.fields(self)])
  </code></pre>