{"assignment":{"_schema_version":2,"course_id":37,"date_created":"2023-09-10T12:50:48.583156+00:00","date_modified":"2023-09-19T16:07:09.171302+00:00","extra_instructor_files":"","extra_starting_files":"","forked_id":null,"forked_version":null,"hidden":false,"id":1431,"instructions":"## Functions Returning Dataclasses\n\n```python returning-dataclasses\nfrom dataclasses import dataclass\nfrom bakery import assert_equal\n\n@dataclass\nclass Card:\n    suit: str\n    rank: int\n\ndef parse_card(card: str) -> Card:\n    return Card(card[0], int(card[1:]))\n\nassert_equal(parse_card(\"H2\"), Card(\"H\", 2))\nassert_equal(parse_card(\"D5\"), Card(\"D\", 5))\n```\n\nFunctions not only can consume dataclasses, but they can also create them.\nIn this example, we have a `parse_card` function that consumes a string value and produces a `Card` instance.\nTo do so, the function must call the `Card` constructor and pass in the appropriate fields.\nIn this case, that means the first character of the string as the first argument, and then the rest of the string converted to an integer for the second field.\nIn the `assert_equal`, we call the function and then assert that the result is the same as calling the constructor with the literal values.\nIn this way, we have a function that can return a dataclass \u2014 specifically, a new instance of the dataclass.\nThe utility of a Dataclass is that we can bundle related fields together to represent a more complex object and pass them conveniently around the program without having to have a huge number of variables.\n\n## Mutable Updates\n\n```python mutate-dataclass\nfrom bakery import assert_equal\nfrom dataclasses import dataclass\n@dataclass\nclass Rectangle:\n    length: int\n    width: int\n\ndef stretch(rect: Rectangle) -> Rectangle:\n    rect.length = rect.length * 2\n    return rect\n\nbox = Rectangle(5, 3)\nassert_equal(stretch(box), Rectangle(10, 3))\n```\n\nOne of the most useful but confusing aspects of a dataclass is that fields' values can also be changed later.\nIn the example shown here, the `stretch` function consumes and produces the same `Rectangle`.\nHowever, before returning the `Rectangle`, its `length` field is doubled from its previous value.\nThe syntax for updating the fields of a dataclass are almost the same as updating the value of a variable.\n\n## Mutability and the Stack/Heap\n\n![A stack/heap diagram where the function is modifying the instance passed in.](bakery_structures_dataclass_returns_mutate_copy.png)\n\nWhat do we mean by \"the same Rectangle\"?\nThis Stack/Heap diagram gives some idea.\nThe `stretch` function has its own `rect` parameter, but the instance is the same as the instance pointed to by the `box` variable in the global scope.\nThe return value comes from the `rect` instance, so the `wide_box` variable also ends up holding the same instance.\nSo, the function manipulates the dataclass coming in instead of creating a new one.\nAs we will discuss later in the section about mutability, there are many dangers to mutably updating fields like this.\nTherefore, we will look at an alternative.\n\n## Immutable Copies\n\n```python copy-dataclass\nfrom bakery import assert_equal\nfrom dataclasses import dataclass\n\n@dataclass\nclass Rectangle:\n    length: int\n    width: int\n\ndef stretch(rect: Rectangle) -> Rectangle:\n    new_rect = Rectangle(rect.length*2, rect.width)\n    return new_rect\n\nbox = Rectangle(5, 3)\nprint(stretch(box))\nprint(box)\n```\n\nA common way to avoid mutation is to create a brand-new instance with new values instead of updating the old instance.\nThis may sound wasteful, but fortunately Python is quite fast at creating new instances.\nAnd in turn, we can use the new instance without worrying about the original being affected.\nConsider this new version of `stretch` that creates a new `Rectangle` instance using the constructor instead of returning the original `rect`.\n\n## Immutability and the Stack/Heap\n\n![A stack/heap diagram of a function NOT modifying the instance that was passed in.](bakery_structures_dataclass_returns_immutable_copy.png)\n\nCompare the diagram from before with the following new diagram.\nIn the mutable version, there was only one `Rectangle` instance, which ended up being pointed to by all the variables.\nBut in this version, there's a second `Rectangle` instance created during the `stretch` function and stored in its `new_rect` local variable.\nThat instance is returned, so the `wide_box` global variable points to the longer `Rectangle` instance.\nThe original `box` variable was not overwritten, and the original `Rectangle` was not modified.\n\nUnder what circumstances would you want to modify the original as opposed to to making a copy? The tradeoff might not be clear just yet, but for now, remember that there are two ways to change a dataclass: by making a new one based on an old one, or by editing the fields of an existing one.\n\n## Converting Dataclasses\n\n```python converting-dataclasses\nfrom dataclasses import dataclass\nfrom bakery import assert_equal\n\n@dataclass\nclass Square:\n    color: str\n    width: int\n\n@dataclass\nclass Circle:\n    radius: int\n    color: str\n\ndef square_to_circle(a_square: Square) -> Circle:\n    return Circle(a_square.width, a_square.color)\n\nassert_equal(square_to_circle(Square(\"red\", 5)), Circle(2, \"red\"))\n```\n\nNothing says that the dataclass coming in must be the same type of dataclass being returned.\nYou might think of a function that takes in one type of dataclass and returns another as a \"conversion\" function, just like the `int` and `str` functions that we have seen before.\nHowever, these kinds of functions are often simpler than they might seem from the outside.\n\nThe `square_to_circle` function takes in a `Square` and returns a `Circle`.\nTo do so, the function must access the attributes of the `a_square` parameter and pass them as arguments to the `Circle` constructor.\nThe body does not have any magic code that \"converts\" the entire `a_square` instance all at once; instead, it unpacks the fields into the constructor function.\nThose fields could be manipulated in more complicated expressions, but fundamentally the process is just like creating any other instance of a dataclass.\n\n## Summary\n\n- Functions can both consume AND return instances of dataclasses, just like they were any other type.\n- To return an instance of a dataclass, the function can either return the original instance that was passed in an as an argument or the function can return a new instance created with the constructor.\n- Instances of a dataclass can have the data in their attributes modified using assignment statements, just like regular variables.\n- Dataclasses cannot be converted directly to other dataclasses; instead you must call the new dataclasses constructor function, access the appropriate fields of the original dataclass, and pass the results in as arguments to the constructor.\n\n","ip_ranges":"","name":"4A3) Returning Dataclasses Reading","on_change":"","on_eval":"","on_run":"","owner_id":1,"owner_id__email":"acbart@udel.edu","points":0,"public":true,"reviewed":false,"sample_submissions":[],"settings":"{\n  \"small_layout\": true,\n  \"header\": \"Returning Dataclasses\",\n  \"slides\": \"bakery_structures_returning_dataclasses.pdf\",\n  \"video\": {\n    \"Bart\": \"https://blockpy.cis.udel.edu/videos/bakery_structures_returning_dataclasses-Bart.mp4\",\n    \"Amy\": \"https://blockpy.cis.udel.edu/videos/bakery_structures_returning_dataclasses-Amy.mp4\"\n  }\n}","starting_code":"","subordinate":true,"tags":[],"type":"reading","url":"bakery_structures_returning_dataclasses_read","version":4},"ip":"216.73.216.157","submission":{"_schema_version":3,"assignment_id":1431,"assignment_version":4,"attempts":0,"code":"","correct":false,"course_id":37,"date_created":"2026-05-21T00:01:09.945226+00:00","date_due":"","date_graded":"","date_locked":"","date_modified":"2026-05-21T00:01:09.945226+00:00","date_started":"","date_submitted":"","endpoint":"","extra_files":"","feedback":"","grading_status":"NotReady","id":2037105,"score":0.0,"submission_status":"Started","time_limit":"","url":"submission_url-49d6bdf0-da3a-46d5-8648-2fcdfbaf50f4","user_id":2044774,"user_id__email":"","version":0},"success":true}
