We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `plasmapy/particles/elements.py`
Content:
```
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 FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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):

{"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"}], "after_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\nfrom dataclasses import dataclass\n\n\n@dataclass\nclass _PeriodicTable:\n    group: int\n    period: int\n    block: str\n    category: str\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"}]}