# This Python file uses the following encoding: utf-8
"""
The :mod:`galactic.context.mixins` package defines mixins classes for defining new types of
contexts:
* :class:`ConcreteIndividual` for defining individuals that own their identifier as a field
* :class:`ConcreteAttribute` for defining attributes that own their name and their type as fields
* :class:`ContextHolder` for defining elements that own their context as a field
* :class:`PopulationHolder` for defining elements that own their population as a field
* :class:`ModelHolder` for defining elements that own their model as a field
* :class:`AttributesHolder` for defining models that own their attributes as a field
* :class:`IndividualsHolder` for defining population that own their individuals as a field
* :class:`ValuesHolder` for defining individuals that own their values as a field
They are widely used for defining
* :class:`MemoryContext <galactic.context.memory.MemoryContext>`
* :class:`MemoryModel <galactic.context.memory.MemoryModel>`
* :class:`MemoryPopulation <galactic.context.memory.MemoryPopulation>`
* :class:`MemoryAttribute <galactic.context.memory.MemoryAttribute>`
* :class:`MemoryIndividual <galactic.context.memory.MemoryIndividual>`
in the :mod:`galactic.context.memory` package.
.. versionadded:: 0.0.1
"""
from typing import Generic, MutableMapping, Iterator
from galactic.context import C, P, M, A, X
# pylint: disable=too-few-public-methods
class _Mixin(object):
def __init__(self, **_):
super().__init__()
# pylint: disable=too-few-public-methods,function-redefined
[docs]class ConcreteIndividual(_Mixin):
"""
The :class:`ConcreteIndividual` class is a mixin used in subclassing the
:class:`Individual <galactic.context.Individual>` class for storing their identifier as a field.
.. versionadded:: 0.0.1
"""
[docs] def __init__(self, **kwargs):
"""
Initialise an individual.
Keyword Arguments
-----------------
identifier : :class:`str`
the individual identifier
.. versionadded:: 0.0.1
"""
super().__init__(**kwargs)
self._identifier = kwargs['identifier']
@property
def identifier(self) -> str:
"""
Get the individual identifier.
Returns
-------
the individual identifier : :class:`str`
.. versionadded:: 0.0.1
"""
return self._identifier
# pylint: disable=too-few-public-methods
[docs]class ConcreteAttribute(_Mixin):
"""
The :class:`ConcreteAttribute` class is a mixin used in subclassing the
:class:`Attribute <galactic.context.Attribute>` class for storing their name and their type
as a class.
.. versionadded:: 0.0.1
"""
[docs] def __init__(self, **kwargs):
"""
Initialise an attribute
Keyword Arguments
-----------------
name : :class:`str`
the attribute name
type : :class:`type <python:type>`
the attribute type
.. versionadded:: 0.0.1
"""
super().__init__(**kwargs)
self._name = kwargs['name']
self._type = kwargs['type']
@property
def name(self) -> str:
"""
Get the attribute name.
Returns
-------
the attribute name : :class:`str`
.. versionadded:: 0.0.1
"""
return self._name
@property
def type(self) -> type:
"""
Get the attribute type.
Returns
-------
the attribute type : :class:`type <python:type>`
.. versionadded:: 0.0.1
"""
return self._type
# pylint: disable=too-few-public-methods
[docs]class ContextHolder(_Mixin, Generic[C]):
"""
The :class:`ContextHolder[C] <ContextHolder>` is a mixin used for storing an element context as
a field.
It's a generic class that depends of a :class:`Context <galactic.context.Context>` subclass
:class:`C`.
.. versionadded:: 0.0.1
"""
[docs] def __init__(self, **kwargs):
"""
Initialise an element by setting its context.
Keyword Arguments
-----------------
context : :class:`C`
the context
.. versionadded:: 0.0.1
"""
super().__init__(**kwargs)
self._context = kwargs['context']
# noinspection PyTypeChecker
@property
def context(self) -> C:
"""
Get the context.
Returns
-------
the context : :class:`C`
.. versionadded:: 0.0.1
"""
return self._context
# pylint: disable=too-few-public-methods
[docs]class PopulationHolder(_Mixin, Generic[P]):
"""
The :class:`PopulationHolder[P] <PopulationHolder>` is a mixin used for storing an element
population as a field.
It's a generic class that depends of a :class:`Population <galactic.context.Population>`
subclass :class:`P`.
.. versionadded:: 0.0.1
"""
[docs] def __init__(self, **kwargs):
"""
Initialise an element by setting its population.
Keyword Arguments
-----------------
population: :class:`P`
the population
.. versionadded:: 0.0.1
"""
super().__init__(**kwargs)
self._population = kwargs['population']
# noinspection PyTypeChecker
@property
def population(self) -> P:
"""
Get the population.
Returns
-------
the population : :class:`P`
.. versionadded:: 0.0.1
"""
return self._population
# pylint: disable=too-few-public-methods
[docs]class ModelHolder(_Mixin, Generic[M]):
"""
The :class:`ModelHolder[M] <ModelHolder>` class is a mixin used for storing an element model as
a field.
It's a generic class that depends of a :class:`Model <galactic.context.Model>` subclass
:class:`M`.
.. versionadded:: 0.0.1
"""
[docs] def __init__(self, **kwargs):
"""
Initialise an element by setting its model.
Keyword Arguments
-----------------
model : :class:`M`
the model
.. versionadded:: 0.0.1
"""
super().__init__(**kwargs)
self._model = kwargs['model']
# noinspection PyTypeChecker
@property
def model(self) -> M:
"""
Get the model.
Returns
-------
the model : :class:`M`
.. versionadded:: 0.0.1
"""
return self._model
# pylint: disable=too-few-public-methods
[docs]class AttributesHolder(_Mixin, Generic[A], MutableMapping[str, A]):
"""
The :class:`AttributesHolder[A] <AttributesHolder>` class is a mixin used for storing the model
attributes in memory.
It's a generic class that depends of an :class:`Attribute <galactic.context.Attribute>`
subclass :class:`A`.
.. versionadded:: 0.0.1
"""
[docs] def __init__(self, **kwargs):
"""
Initialise an attribute holder.
Keyword Arguments
-----------------
attributes : :class:`Iterable[A] <python:collections.abc.Iterable>`
the attributes
.. versionadded:: 0.0.1
"""
super().__init__(**kwargs)
self._attributes = {attribute.name: attribute for attribute in kwargs['attributes']}
def __getitem__(self, name: str) -> A:
"""
Get an attribute using its name
Parameters
----------
name : :class:`str`
the attribute name
Returns
-------
the attribute : A
Raises
------
KeyError
if this attributes holder does not contain an attribute with this name.
.. versionadded:: 0.0.1
"""
return self._attributes[name]
def __setitem__(self, name: str, attribute: A) -> None:
"""
Set an attribute using its name
Parameters
----------
name : :class:`str`
the attribute name
attribute: :class:`A`
.. versionadded:: 0.0.1
"""
self._attributes[name] = attribute
def __delitem__(self, name: str) -> None:
"""
Delete an attribute using its name
Parameters
----------
name : :class:`str`
the attribute name
Raises
------
KeyError
if this attributes holder does not contain an attribute with this name.
.. versionadded:: 0.0.1
"""
del self._attributes[name]
def __iter__(self) -> Iterator[str]:
"""
Get an iterator over the attribute names.
Returns
-------
an iterator : :class:`Iterator[str] <python:collections.abc.Iterator>`
.. versionadded:: 0.0.1
"""
return iter(self._attributes)
def __len__(self) -> int:
"""
Get the number of attributes.
Returns
-------
the number of attributes : :class:`int`
.. versionadded:: 0.0.1
"""
return len(self._attributes)
def __bool__(self):
"""
Get the boolean value of an attribute holder.
Returns
-------
the boolean value : :class:`bool`
.. versionadded:: 0.0.1
"""
return bool(self._attributes)
# pylint: disable=too-few-public-methods
[docs]class IndividualsHolder(_Mixin, Generic[X], MutableMapping[str, X]):
"""
The :class:`IndividualsHolder[X] <IndividualsHolder>` class is a mixin used for storing the
population individuals in memory.
It's a generic class that depends of an :class:`Individual <galactic.context.Individual>`
subclass :class:`X`.
.. versionadded:: 0.0.1
"""
[docs] def __init__(self, **kwargs):
"""
Initialise an individuals holder.
Keyword Arguments
-----------------
individuals : :class:`Iterable[A] <python:collections.abc.Iterable>`
the individuals
.. versionadded:: 0.0.1
"""
super().__init__(**kwargs)
self._individuals = {individual.identifier: individual for individual in
kwargs['individuals']}
def __getitem__(self, identifier: str) -> X:
"""
Get an individual using its identifier.
Parameters
----------
identifier : :class:`str`
the individual identifier
Returns
-------
the individual : :class: `X`
Raises
------
KeyError
if the identifier does not belong to the individuals holder.
.. versionadded:: 0.0.1
"""
return self._individuals[identifier]
def __delitem__(self, identifier: str) -> None:
"""
Delete an individual using its identifier.
Parameters
----------
identifier : :class:`str`
the individual identifier
Raises
------
KeyError
if the identifier does not belong to the individuals holder.
.. versionadded:: 0.0.1
"""
del self._individuals[identifier]
def __setitem__(self, identifier: str, individual: X) -> None:
"""
Set an individual using its identifier.
Parameters
----------
identifier : :class:`str`
the individual identifier
individual: :class:`X`
an individual
.. versionadded:: 0.0.1
"""
self._individuals[identifier] = individual
def __iter__(self) -> Iterator[str]:
"""
Get an iterator over the individual identifiers.
Returns
-------
an iterator : :class:`Iterator[str] <python:collections.abc.Iterator>`
.. versionadded:: 0.0.1
"""
return iter(self._individuals)
def __len__(self) -> int:
"""
Get the number of individuals.
Returns
-------
the number of indidivuals : :class:`int`
.. versionadded:: 0.0.1
"""
return len(self._individuals)
def __bool__(self) -> bool:
"""
Get the boolean representation of the individuals holder.
Returns
-------
the boolean representation : :class:`bool`
.. versionadded:: 0.0.1
"""
return bool(self._individuals)
# pylint: disable=too-few-public-methods
[docs]class ValuesHolder(_Mixin):
"""
The :class:`ValuesHolder[A] <ValuesHolder>` class is a mixin for storing the individual values
in memory.
It's a generic class that depends of an :class:`Attribute <galactic.context.Attribute>` subclass
:class:`A`.
.. versionadded:: 0.0.1
"""
[docs] def __init__(self, **kwargs):
"""
Initialise a values holder.
Keyword Arguments
-----------------
values : :class:`Mapping[str, object] <python:collections.abc.Mapping>`
the initial (name, value) pairs
.. versionadded:: 0.0.1
"""
super().__init__(**kwargs)
self._values = {name: value for name, value in kwargs['values'].items()}
def __getitem__(self, name: str) -> object:
"""
Get a value using the attribute name.
Parameters
----------
name : :class:`str`
the attribute name
Returns
-------
the value : :class:`object`
Raises
------
KeyError
if the name does not belong to the values holder.
.. versionadded:: 0.0.1
"""
return self._values[name]
def __setitem__(self, name: str, value) -> None:
"""
Set the value of an attribute.
Parameters
----------
name : :class:`str`
the attribute name
value : :class:`object`
the new value
.. versionadded:: 0.0.1
"""
self._values[name] = value
def __delitem__(self, name: str):
"""
Delete a value using the attribute name.
Parameters
----------
name : :class:`str`
the attribute name
Raises
------
KeyError
if the name does not belong to the values holder.
.. versionadded:: 0.0.1
"""
del self._values[name]