prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Pickle-able Particles (and other objects, for parallelization)
  My draft Thomson scattering fitting function (#974) is currently restricted to using 1 CPU core because the underlying fit function (`thomson.spectral_density`) contains instances of the `Particle` object, which is not pickle-able because it includes a lambda function. If this issue was resolved, enabling parallelization of the fit function would be as simple as including a keyword to `scipy.optimize.differential_evolution`. 
  
  Here is the error if I try to turn on parallelization now:
  
  ```
  AttributeError: Can't pickle local object 'Particle.__init__.<locals>.<lambda>
  ```
  
  and here is (I think) the offending line in `Particle.__init__`:
  
  ```
  Line 408    -->    self._attributes = defaultdict(lambda: None)
  ``` 
  
  
  Pickling a model that's then passed between multiple worker pools is, I gather, a pretty common way of parallelizing Python code, so I think it would be very desirable (maybe even a requirement) for all of these basic classes to be serializable. I know there are other serialization modules that DO support lambdas (eg. dill, cloudpickle), but they don't help since the issue is what's compatible with Scipy.
  
  @namurphy  Is this something that can be easily changed?
  
  
  </issue>
  <code>
  [start of plasmapy/particles/elements.py]
  1 """
  2 Module for loading atomic data for elements from
  3 :file:`plasmapy/particles/data/elements.json`.
  4 
  5 The periodic tabla data is from: http://periodic.lanl.gov/index.shtml
  6 
  7 .. attention::
  8     This module is not part of PlasmaPy's public API.
  9 """
  10 __all__ = []
  11 
  12 import astropy.units as u
  13 import collections
  14 import json
  15 import pkgutil
  16 
  17 _PeriodicTable = collections.namedtuple(
  18     "periodic_table", ["group", "category", "block", "period"]
  19 )
  20 
  21 
  22 def _element_obj_hook(obj):
  23     if "unit" in obj:
  24         return obj["value"] * u.Unit(obj["unit"])
  25     return obj
  26 
  27 
  28 # this code was used to create the JSON file as per vn-ki on Matrix:
  29 # https://matrix.to/#/!hkWCiyhQyxiYJlUtKF:matrix.org/
  30 #    $1554667515670438wIKlP:matrix.org?via=matrix.org&via=cadair.com
  31 #
  32 # def plasma_default(obj):
  33 #     if isinstance(obj, u.Quantity):
  34 #         return {
  35 #             "unit": obj.unit.name,
  36 #             "value": obj.value,
  37 #         }
  38 #
  39 # with open("elements.json", "w") as f:
  40 #    json.dump(_Elements, f, default=plasma_default, indent=2)
  41 
  42 
  43 _elements = json.loads(
  44     pkgutil.get_data("plasmapy", "particles/data/elements.json"),
  45     object_hook=_element_obj_hook,
  46 )
  47 
  48 
  49 _atomic_numbers_to_symbols = {
  50     elemdict["atomic number"]: symb for (symb, elemdict) in _elements.items()
  51 }
  52 
  53 _element_names_to_symbols = {
  54     elemdict["element name"]: symb for (symb, elemdict) in _elements.items()
  55 }
  56
  [end of plasmapy/particles/elements.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/plasmapy/particles/elements.py b/plasmapy/particles/elements.py
  --- a/plasmapy/particles/elements.py
  +++ b/plasmapy/particles/elements.py
  @@ -14,9 +14,15 @@
   import json
   import pkgutil
   
  -_PeriodicTable = collections.namedtuple(
  -    "periodic_table", ["group", "category", "block", "period"]
  -)
  +from dataclasses import dataclass
  +
  +
  +@dataclass
  +class _PeriodicTable:
  +    group: int
  +    period: int
  +    block: str
  +    category: str
   
   
   def _element_obj_hook(obj):
  
verification_info: {"golden_diff": "diff --git a/plasmapy/particles/elements.py b/plasmapy/particles/elements.py\n--- a/plasmapy/particles/elements.py\n+++ b/plasmapy/particles/elements.py\n@@ -14,9 +14,15 @@\n import json\n import pkgutil\n \n-_PeriodicTable = collections.namedtuple(\n-    \"periodic_table\", [\"group\", \"category\", \"block\", \"period\"]\n-)\n+from dataclasses import dataclass\n+\n+\n+@dataclass\n+class _PeriodicTable:\n+    group: int\n+    period: int\n+    block: str\n+    category: str\n \n \n def _element_obj_hook(obj):\n", "issue": "Pickle-able Particles (and other objects, for parallelization)\nMy draft Thomson scattering fitting function (#974) is currently restricted to using 1 CPU core because the underlying fit function (`thomson.spectral_density`) contains instances of the `Particle` object, which is not pickle-able because it includes a lambda function. If this issue was resolved, enabling parallelization of the fit function would be as simple as including a keyword to `scipy.optimize.differential_evolution`. \r\n\r\nHere is the error if I try to turn on parallelization now:\r\n\r\n```\r\nAttributeError: Can't pickle local object 'Particle.__init__.<locals>.<lambda>\r\n```\r\n\r\nand here is (I think) the offending line in `Particle.__init__`:\r\n\r\n```\r\nLine 408    -->    self._attributes = defaultdict(lambda: None)\r\n``` \r\n\r\n\r\nPickling a model that's then passed between multiple worker pools is, I gather, a pretty common way of parallelizing Python code, so I think it would be very desirable (maybe even a requirement) for all of these basic classes to be serializable. I know there are other serialization modules that DO support lambdas (eg. dill, cloudpickle), but they don't help since the issue is what's compatible with Scipy.\r\n\r\n@namurphy  Is this something that can be easily changed?\r\n\n", "before_files": [{"content": "\"\"\"\nModule for loading atomic data for elements from\n:file:`plasmapy/particles/data/elements.json`.\n\nThe periodic tabla data is from: http://periodic.lanl.gov/index.shtml\n\n.. attention::\n    This module is not part of PlasmaPy's public API.\n\"\"\"\n__all__ = []\n\nimport astropy.units as u\nimport collections\nimport json\nimport pkgutil\n\n_PeriodicTable = collections.namedtuple(\n    \"periodic_table\", [\"group\", \"category\", \"block\", \"period\"]\n)\n\n\ndef _element_obj_hook(obj):\n    if \"unit\" in obj:\n        return obj[\"value\"] * u.Unit(obj[\"unit\"])\n    return obj\n\n\n# this code was used to create the JSON file as per vn-ki on Matrix:\n# https://matrix.to/#/!hkWCiyhQyxiYJlUtKF:matrix.org/\n#    $1554667515670438wIKlP:matrix.org?via=matrix.org&via=cadair.com\n#\n# def plasma_default(obj):\n#     if isinstance(obj, u.Quantity):\n#         return {\n#             \"unit\": obj.unit.name,\n#             \"value\": obj.value,\n#         }\n#\n# with open(\"elements.json\", \"w\") as f:\n#    json.dump(_Elements, f, default=plasma_default, indent=2)\n\n\n_elements = json.loads(\n    pkgutil.get_data(\"plasmapy\", \"particles/data/elements.json\"),\n    object_hook=_element_obj_hook,\n)\n\n\n_atomic_numbers_to_symbols = {\n    elemdict[\"atomic number\"]: symb for (symb, elemdict) in _elements.items()\n}\n\n_element_names_to_symbols = {\n    elemdict[\"element name\"]: symb for (symb, elemdict) in _elements.items()\n}\n", "path": "plasmapy/particles/elements.py"}]}