Source code for free_properties._property_array

# -*- coding: utf-8 -*-
"""
Created on Thu Jan 10 03:56:02 2019

@author: yoelr
"""
import numpy as np

__all__ = ('property_array',)

ndarray = np.ndarray
isa = isinstance

# %% Property array

[docs]class property_array(ndarray): """ Create an array that allows for array-like manipulation of FreeProperty objects. All entries in a property_array must be instances of FreeProperty. Setting items of a property_array sets values of objects instead. Parameters ---------- properties : array_like[FreeProperty] Examples -------- Use the PropertyFactory to create a Weight property class which calculates weight based on density and volume: .. code-block:: python >>> from free_property import PropertyFactory, property_array >>> >>> @PropertyFactory >>> def Weight(self): ... '''Weight (kg) based on volume (m^3).''' ... data = self.data ... rho = data['rho'] # Density (kg/m^3) ... vol = data['vol'] # Volume (m^3) ... return rho * vol >>> >>> @Weight.setter >>> def Weight(self, weight): ... data = self.data ... rho = data['rho'] # Density (kg/m^3) ... data['vol'] = weight / rho Create dictionaries of data and initialize new properties: .. code-block:: python >>> water_data = {'rho': 1000, 'vol': 3} >>> ethanol_data = {'rho': 789, 'vol': 3} >>> weight_water = Weight('Water', water_data) >>> weight_ethanol = Weight('Ethanol', ethanol_data) >>> weight_water <Weight(Water): 3000 kg> >>> weight_ethanol <Weight(Ethanol): 2367 kg> Create a property_array from data: .. code-block:: python >>> prop_arr = property_array([weight_water, weight_ethanol]) >>> prop_arr property_array([3000.0, 2367.0]) Changing the values of a property_array changes the value of its properties: .. code-block:: python >>> # Addition in place >>> prop_arr += 3000 >>> prop_arr property_array([6000.0, 5367.0]) >>> # Note how the data also changes >>> water_data {'rho': 1000, 'vol': 6.0} >>> ethanol_data {'rho': 789, 'vol': 6.802281368821292} >>> # Setting an item changes the property value >>> prop_arr[1] = 2367 >>> ethanol_data {'rho': 789, 'vol': 3} New arrays have no connection to the property_array: .. code-block:: python >>> prop_arr - 1000 # Returns a new array array([5000.0, 1367.0]) >>> water_data # Data remains unchanged {'rho': 1000, 'vol': 6.0} """ def __new__(cls, properties): return np.asarray(properties, dtype=object).view(cls) @property def value(self): return np.array(self, float)
[docs] def all(self, *args, **kwargs): return np.array(self, float).all(*args, **kwargs)
[docs] def any(self, *args, **kwargs): return np.array(self, float).any(*args, **kwargs)
[docs] def copy(self): return np.array(self, float)
[docs] def argmax(self, *args, **kwargs): return np.array(self, float).argmax(*args, **kwargs)
[docs] def argmin(self, *args, **kwargs): return np.array(self, float).argmin(*args, **kwargs)
[docs] def argpartition(self, *args, **kwargs): return np.array(self, float).argpartition(*args, **kwargs)
[docs] def argsort(self, *args, **kwargs): return np.array(self, float).argsort(*args, **kwargs)
[docs] def choose(self, *args, **kwargs): return np.array(self, float).choose(*args, **kwargs)
[docs] def clip(self, *args, **kwargs): return np.array(self, float).clip(*args, **kwargs)
[docs] def conj(self): return np.array(self, float).conj()
[docs] def conjugate(self): return np.array(self, float).conjugate()
[docs] def cumprod(self, *args, **kwargs): return np.array(self, float).cumprod(*args, **kwargs)
[docs] def cumsum(self, *args, **kwargs): return np.array(self, float).cumsum(*args, **kwargs)
[docs] def dot(self, *args, **kwargs): return np.array(self, float).dot(*args, **kwargs)
[docs] def max(self, *args, **kwargs): return np.array(self, float).max(*args, **kwargs)
[docs] def mean(self, *args, **kwargs): return np.array(self, float).mean(*args, **kwargs)
[docs] def min(self, *args, **kwargs): return np.array(self, float).min(*args, **kwargs)
[docs] def nonzero(self, *args, **kwargs): return np.array(self, float).nonzero(*args, **kwargs)
[docs] def prod(self, *args, **kwargs): return np.array(self, float).prod(*args, **kwargs)
[docs] def ptp(self, *args, **kwargs): return np.array(self, float).ptp(*args, **kwargs)
[docs] def put(self, *args, **kwargs): return np.array(self, float).put(*args, **kwargs)
[docs] def round(self, *args, **kwargs): return np.array(self, float).round(*args, **kwargs)
[docs] def std(self, *args, **kwargs): return np.array(self, float).std(*args, **kwargs)
[docs] def sum(self, *args, **kwargs): return np.array(self, float).sum(*args, **kwargs)
[docs] def trace(self, *args, **kwargs): return np.array(self, float).trace(*args, **kwargs)
[docs] def var(self, *args, **kwargs): return np.array(self, float).var(*args, **kwargs)
def __getitem__(self, key): item = self.base[key] base = item.base if base is None: return np.array(item, float) elif base.base is base: # Must be a free property return item.value else: # Must be a property array return item.view(property_array) def __setitem__(self, key, value): items = self.base[key] if isa(items, ndarray): for i, v in np.nditer((items, value), flags=('refs_ok', 'zerosize_ok')): i.item().value = v else: items.value = value def __add__(self, other): return np.array(self, float) + other def __sub__(self, other): return np.array(self, float) - other def __mul__(self, other): return np.array(self, float) * other def __matmul__(self, other): return np.array(self, float) @ other def __truediv__(self, other): return np.array(self, float) / other def __floordiv__(self, other): return np.array(self, float) // other def __mod__(self, other): return np.array(self, float) % other def __pow__(self, other): return np.array(self, float) ** other def __lshift__(self, other): return np.array(self, float) << other def __rshift__(self, other): return np.array(self, float) >> other def __and__(self, other): return np.array(self, float) & other def __xor__(self, other): return np.array(self, float) ^ other def __or__(self, other): return np.array(self, float) | other def __radd__(self, other): return other + np.array(self, float) def __rsub__(self, other): return other - np.array(self, float) def __rmul__(self, other): return other * np.array(self, float) def __rmatmul__(self, other): return other @ np.array(self, float) def __rtruediv__(self, other): return other / np.array(self, float) def __rfloordiv__(self, other): return other // np.array(self, float) def __rmod__(self, other): return other % np.array(self, float) def __rpow__(self, other): return other ** np.array(self, float) def __rlshift__(self, other): return other << np.array(self, float) def __rrshift__(self, other): return other >> np.array(self, float) def __rand__(self, other): return other & np.array(self, float) def __rxor__(self, other): return other ^ np.array(self, float) def __ror__(self, other): return other | np.array(self, float) def __iadd__(self, other): self[:] = np.array(self, float) + other return self def __isub__(self, other): self[:] = np.array(self, float) - other return self def __imul__(self, other): self[:] = np.array(self, float) * other return self def __imatmul__(self, other): raise TypeError("in-place matrix multiplication is not (yet) supported") def __itruediv__(self, other): self[:] = np.array(self, float) / other return self def __ifloordiv__(self, other): self[:] = np.array(self, float) // other return self def __imod__(self, other): self[:] =np.array(self, float) % other return self def __ipow__(self, other): self[:] = np.array(self, float) ** other return self def __ilshift__(self, other): self[:] = np.array(self, float) << other return self def __irshift__(self, other): self[:] = np.array(self, float) >> other return self def __iand__(self, other): self[:] = np.array(self, float) & other return self def __ixor__(self, other): self[:] = np.array(self, float) ^ other return self def __ior__(self, other): self[:] = np.array(self, float) | other return self def __repr__(self): return super().__repr__().replace('dtype=object)', '').rstrip("\n, ") + ')'