Cleaned up the __init__.
Instead of asking for permission to change an attribute, we're eager and fix it afterwards using the dataclass post init.
This commit is contained in:
@ -121,11 +121,13 @@ class AttributesData:
|
|||||||
|
|
||||||
Additionally gains dataclass features, including an __init__ and __repr__ to avoid boilerplate code.
|
Additionally gains dataclass features, including an __init__ and __repr__ to avoid boilerplate code.
|
||||||
|
|
||||||
Developer Note:
|
Developer Notes:
|
||||||
|
|
||||||
See the Attributes class for default methods.
|
See the Attributes class for default methods.
|
||||||
|
|
||||||
Override this class to set default attributes. See help(Attributes) for more information.
|
Override this class to set default attributes. See help(Attributes) for more information.
|
||||||
|
|
||||||
|
If you must override the __post_init__, don't forget to use super().__post_init__().
|
||||||
"""
|
"""
|
||||||
|
|
||||||
run: int = 0
|
run: int = 0
|
||||||
@ -201,40 +203,11 @@ class AttributesData:
|
|||||||
|
|
||||||
graph: Callable[[Database], Graph] = matplotlib_graph.Matplotlib_Graph
|
graph: Callable[[Database], Graph] = matplotlib_graph.Matplotlib_Graph
|
||||||
|
|
||||||
def __init__(self: AttributesData, *args: Any, **kwargs: Any) -> None:
|
def __post_init__(self: AttributesData) -> None:
|
||||||
"""
|
"""Undo any instance attributes that are None when they should be methods from the class."""
|
||||||
Generated AttributesData.__init__, accepts dataclass fields as parameters.
|
for name in self.__dataclass_fields__:
|
||||||
Ignores parameters whose values are None if something is already set.
|
if getattr(self, name) is None and isinstance(getattr(type(self), name), AsMethod):
|
||||||
"""
|
delattr(self, name)
|
||||||
# Extract the default value from a dataclass field.
|
|
||||||
def get_default(default_field):
|
|
||||||
if type(default_field.default) is not _MISSING_TYPE:
|
|
||||||
return default_field.default
|
|
||||||
else:
|
|
||||||
return default_field.default_factory()
|
|
||||||
# Extract the default values from all fields.
|
|
||||||
defaults = {name: get_default(default_field) for name, default_field in self.__dataclass_fields__.items() if default_field.init}
|
|
||||||
# Hard defaults cannot be a given parameter in the __init__.
|
|
||||||
hard_defaults = {name: get_default(default_field) for name, default_field in self.__dataclass_fields__.items() if not default_field.init}
|
|
||||||
# Verify not too many args are passed in.
|
|
||||||
if len(args) > len(defaults):
|
|
||||||
raise TypeError(f"__init__ takes {len(defaults)} positional arguments but {len(args)} were given")
|
|
||||||
# Convert the args to kwargs.
|
|
||||||
args = dict(zip(defaults, args))
|
|
||||||
# Verify a parameter is not in both the args and kwargs.
|
|
||||||
for name in args.keys() & kwargs.keys():
|
|
||||||
raise TypeError(f"__init__() got multiple values for '{name}'")
|
|
||||||
# Verify all kwargs are in the fields.
|
|
||||||
for name in kwargs.keys() - self.__dataclass_fields__.keys():
|
|
||||||
raise TypeError(f"__init__ got an unexpected keyword argument '{name}'")
|
|
||||||
# Merge the defaults, args, and kwargs.
|
|
||||||
for name, value in {**defaults, **args, **kwargs, **hard_defaults}.items():
|
|
||||||
# Ignore None values if self already has that attribute.
|
|
||||||
if value is not None or name not in dir(self):
|
|
||||||
setattr(self, name, value)
|
|
||||||
# Run the __post_init__.
|
|
||||||
if hasattr(self, "__post_init__"):
|
|
||||||
self.__post_init__()
|
|
||||||
|
|
||||||
|
|
||||||
class AsMethod:
|
class AsMethod:
|
||||||
@ -251,7 +224,7 @@ class AsMethod:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.default = default
|
self.default = default
|
||||||
|
|
||||||
def __get__(self: AsMethod, obj: "AttributesProperties", cls: type) -> Callable:
|
def __get__(self: AsMethod, obj: "Attributes", cls: type) -> Callable:
|
||||||
# Already has the attribute on the object.
|
# Already has the attribute on the object.
|
||||||
if self.name in vars(obj):
|
if self.name in vars(obj):
|
||||||
return vars(obj)[self.name]
|
return vars(obj)[self.name]
|
||||||
@ -261,7 +234,7 @@ class AsMethod:
|
|||||||
# Otherwise use the default as a function.
|
# Otherwise use the default as a function.
|
||||||
return self.default
|
return self.default
|
||||||
|
|
||||||
def __set__(self: AsMethod, obj: "AttributesProperties", method: Optional[Callable]) -> None:
|
def __set__(self: AsMethod, obj: "Attributes", method: Optional[Callable]) -> None:
|
||||||
if method is None:
|
if method is None:
|
||||||
return
|
return
|
||||||
elif not callable(method):
|
elif not callable(method):
|
||||||
@ -270,6 +243,9 @@ class AsMethod:
|
|||||||
method = MethodType(method, obj)
|
method = MethodType(method, obj)
|
||||||
vars(obj)[self.name] = method
|
vars(obj)[self.name] = method
|
||||||
|
|
||||||
|
def __delete__(self: AsMethod, obj: "Attributes") -> None:
|
||||||
|
del vars(obj)[self.name]
|
||||||
|
|
||||||
|
|
||||||
class Attributes(AttributesData):
|
class Attributes(AttributesData):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user