Module phi.struct

Deprecated.

Simplifies working with nested structures of lists, tuples, dicts and objects.

Main functions:

  • map
  • foreach
  • flatten
  • zip
  • names
Expand source code
"""
*Deprecated*.

Simplifies working with nested structures of lists, tuples, dicts and objects.

Main functions:

* map
* foreach
* flatten
* zip
* names

"""

from ._context import unsafe
from ._trait import Trait
from ._structdef import definition, variable, constant, derived
from ._item_condition import DATA, VARIABLES, CONSTANTS, ALL_ITEMS, ignore
from ._struct import Struct, kwargs, to_dict, variables, constants, properties_dict, copy_with, isstruct, equal, VALID, INVALID

# pylint: disable-msg = redefined-builtin
from ._struct_functions import flatten, unflatten, names, map, map_item, foreach, zip, Trace, compare, print_differences, shape, staticshape, dtype, any, all


__all__ = [key for key in globals().keys() if not key.startswith('_')]

Functions

def all(condition_struct)
Expand source code
def all(condition_struct):
    values = flatten(condition_struct)
    for value in values:
        if not value:
            return False
    return True
def any(condition_struct)
Expand source code
def any(condition_struct):
    values = flatten(condition_struct)
    for value in values:
        if value:
            return True
    return False
def compare(structs, leaf_condition=None, recursive=True, item_condition=ALL)
Expand source code
def compare(structs, leaf_condition=None, recursive=True, item_condition=ALL_ITEMS):
    if len(structs) <= 1:
        return []
    result = set()

    def check(trace):
        value = trace.value
        for other in structs[1:]:
            try:
                other_value = trace.find_in(other)
                if not equal(value, other_value):
                    result.add(trace)
            except (ValueError, KeyError, TypeError):
                result.add(trace)
    map(check, structs[0], leaf_condition=leaf_condition, recursive=recursive, trace=True, item_condition=item_condition, content_type=INVALID)
    return result
def constant(default=None, dependencies=(), holds_data=False, **trait_kwargs)

Required decorator for constants_dict of custom structs. The enclosing class must be decorated with struct.definition().

Args

default
default value passed to validation if no other value is specified
dependencies
other items (string or reference for inherited constants_dict) (Default value = ())
holds_data
determines whether the constant is considered by data-related functions (Default value = False)
**trait_kwargs
 

Returns

read-only property

Expand source code
def constant(default=None, dependencies=(), holds_data=False, **trait_kwargs):
    """
    Required decorator for constants_dict of custom structs.
    The enclosing class must be decorated with struct.definition().

    Args:
      default: default value passed to validation if no other value is specified
      dependencies: other items (string or reference for inherited constants_dict) (Default value = ())
      holds_data: determines whether the constant is considered by data-related functions (Default value = False)
      **trait_kwargs: 

    Returns:
      read-only property

    """
    def decorator(validate):
        return Item(validate.__name__, validate, False, default, dependencies, holds_data, **trait_kwargs)
    return decorator
def constants(struct)
Expand source code
def constants(struct):
    if isinstance(struct, (list, tuple, dict, np.ndarray)):
        return {}
    else:
        return to_dict(struct, CONSTANTS)
def copy_with(struct, new_values_dict, change_type=None)
Expand source code
def copy_with(struct, new_values_dict, change_type=None):
    if isinstance(struct, Struct):
        return struct.copied_with(change_type=change_type, **new_values_dict)
    if isinstance(struct, tuple):
        duplicate = list(struct)
        for key, value in new_values_dict.items():
            duplicate[key] = value
        return tuple(duplicate)
    if isinstance(struct, list):
        duplicate = list(struct)
        for key, value in new_values_dict.items():
            duplicate[key] = value
        return duplicate
    if isinstance(struct, np.ndarray) and struct.dtype == np.object:
        duplicate = struct.copy()
        for key, value in new_values_dict.items():
            duplicate[key] = value
        return duplicate
    if isinstance(struct, dict):
        duplicate = dict(struct)
        for key, value in new_values_dict.items():
            duplicate[key] = value
        if type(struct) is dict:
            return duplicate
        else:
            return type(struct)(duplicate)
    raise ValueError("Not a struct: %s" % struct)
def definition(traits=())

Required decorator for custom struct classes.

Args

traits
(Default value = ())

Returns:

Expand source code
def definition(traits=()):
    """
    Required decorator for custom struct classes.

    Args:
      traits:  (Default value = ())

    Returns:

    """
    if isinstance(traits, Trait):
        traits = (traits,)
    else:
        for trait in traits:
            assert isinstance(trait, Trait), 'Illegal trait: %s' % trait
        traits = tuple(traits)

    def decorator(struct_class, traits=traits):
        assert struct_class.__initialized_class__ != struct_class, 'Struct class already initialized: %s' % struct_class
        items = {}
        for attribute_name in dir(struct_class):
            item = getattr(struct_class, attribute_name)
            if isinstance(item, Item):
                items[attribute_name] = item
        # --- Inheritance ---
        inherited_traits = ()
        for base in struct_class.__bases__:
            if base.__name__ != 'Struct' and hasattr(base, '__items__'):
                for item in base.__items__:
                    if item.name not in items:
                        subclassed_item = copy.copy(item)
                        items[item.name] = subclassed_item
                        setattr(struct_class, item.name, subclassed_item)
                for trait in base.__traits__:
                    if trait not in traits:
                        inherited_traits += (trait,)
        traits = inherited_traits + tuple([t for t in traits if t not in inherited_traits])
        assert len(set(traits)) == len(traits), "Duplicate traits on struct class '%s'" % struct_class
        # --- Initialize & Decorate ---
        struct_class.__traits__ = traits
        for item in items.values():
            item.__initialize_for__(struct_class)
        items = _order_by_dependencies(items, struct_class)
        struct_class.__items__ = tuple(items)
        struct_class.__initialized_class__ = struct_class
        # --- Check trait keywords ---
        for item in items:
            for trait_kw, trait_kw_val in item.trait_kwargs.items():
                matching_traits = [trait for trait in traits if trait_kw in trait.keywords]
                if len(matching_traits) == 0:
                    raise ValueError('Trait keyword "%s" does not match any trait of struct %s' % (trait_kw, struct_class.__name__))
                for trait in matching_traits:
                    trait.check_argument(struct_class, item, trait_kw, trait_kw_val)
        return struct_class
    return decorator
def derived()

Derived properties work similar to @property but can be easily broadcast across many instances. :return: read-only property

Args:

Returns:

Expand source code
def derived():
    """
    Derived properties work similar to @property but can be easily broadcast across many instances.
    :return: read-only property

    Args:

    Returns:

    """
    def decorator(getter):
        return DerivedProperty(getter.__name__, getter)
    return decorator
def dtype(obj, leaf_condition=None, item_condition=None)

Maps all values of a struct to their respective data types using math.dtype(). To specify custom dtypes, add an override with key struct.dtype to the Item.

Args

obj
struct or leaf
leaf_condition
optional) leaf_condition passed to map() (Default value = None)
item_condition
optional) item_condition passed to map() (Default value = None)

Returns

Struct of same type holding data types instead of data

Expand source code
def dtype(obj, leaf_condition=None, item_condition=None):
    """
    Maps all values of a struct to their respective data types using `math.dtype()`.
    To specify custom dtypes, add an override with key struct.dtype to the Item.

    Args:
      obj: struct or leaf
      leaf_condition: optional) leaf_condition passed to `map` (Default value = None)
      item_condition: optional) item_condition passed to `map` (Default value = None)

    Returns:
      Struct of same type holding data types instead of data

    """
    def get_dtype(obj):
        try:
            return choose_backend(obj).dtype(obj)
        except NoBackendFound:
            return type(obj)
    if isinstance(obj, Struct):
        if not skip_validate():
            assert obj.content_type is VALID or obj.content_type is INVALID, "dtype can only be accessed on data structs but '%s' has content type '%s'" % (type(obj).__name__, obj.content_type)
    return map(get_dtype, obj, leaf_condition=leaf_condition, item_condition=item_condition, content_type=dtype)
def equal(obj1, obj2)
Expand source code
def equal(obj1, obj2):
    if isinstance(obj1, np.ndarray) or isinstance(obj2, np.ndarray):
        if obj1.dtype != np.object and obj2.dtype != np.object:
            if not np.allclose(obj1, obj2):
                return False
        else:
            if not np.all(obj1 == obj2):
                return False
    else:
        try:
            if obj1 != obj2:
                return False
            # pylint: disable-msg = broad-except  # the exception type can depend on the values
        except (ValueError, BaseException):  # not a boolean result
            if obj1 is not obj2:
                return False
    return True
def flatten(struct, leaf_condition=None, trace=False, item_condition=None)

Generates a list of all leaves by recursively iterating over the given struct.

Args

struct
struct or leaf
leaf_condition
optional) function that determines which structs are treated as leaves. Non-structs are always treated as leaves. (Default value = None)
trace
If True, returns a list of Trace objects instead of values. (Default value = False)
item_condition
optional) ItemCondition or boolean function that filters which Items are accumulated. (Default value = None)

Returns

list containing all leaves in the struct hierarchy

Expand source code
def flatten(struct, leaf_condition=None, trace=False, item_condition=None):
    """
    Generates a list of all leaves by recursively iterating over the given struct.

    Args:
      struct: struct or leaf
      leaf_condition: optional) function that determines which structs are treated as leaves. Non-structs are always treated as leaves. (Default value = None)
      trace: If True, returns a list of Trace objects instead of values. (Default value = False)
      item_condition: optional) ItemCondition or boolean function that filters which Items are accumulated. (Default value = None)

    Returns:
      list containing all leaves in the struct hierarchy

    """
    def map_leaf(value):
        result.append(value)
        return value
    result = []
    map(map_leaf, struct, leaf_condition, recursive=True, trace=trace, item_condition=item_condition, content_type=INVALID)
    return result
def foreach(function, *structs, leaf_condition=None, recursive=True, trace=False, item_condition=None)
Expand source code
def foreach(function, *structs, leaf_condition=None, recursive=True, trace=False, item_condition=None):
    if len(structs) == 1:
        map(function, structs[0], leaf_condition, recursive, trace, item_condition, content_type=INVALID)
    else:
        struct = zip(structs, leaf_condition, item_condition)
        map(function, struct, leaf_condition, recursive, trace, item_condition, content_type=INVALID)
def ignore(items)
Expand source code
def ignore(items):
    if not isinstance(items, (tuple, list)):
        items = (items,)
    for ignored_item in items:
        assert isinstance(ignored_item, Item) or isinstance(ignored_item, str)

    def is_ignored(item):
        for ignored in items:
            if item is ignored:
                return True
            if item.name == ignored:
                return True
        return False

    condition = ItemCondition(lambda item: not is_ignored(item), 'ignore %s' % items)
    return condition
def isstruct(obj, leaf_condition=None)
Expand source code
def isstruct(obj, leaf_condition=None):
    if not isinstance(obj, (Struct, list, tuple, dict, np.ndarray)):
        return False
    if isinstance(obj, np.ndarray) and obj.dtype != np.object:
        return False
    if leaf_condition is not None and leaf_condition(obj):
        return False
    return True
def kwargs(locals, include_self=False, ignore=())
Expand source code
def kwargs(locals, include_self=False, ignore=()):
    # pylint: disable-msg = redefined-builtin
    assert 'kwargs' in locals, "No 'kwargs' variable found in locals. Maybe you forgot to add '**kwargs' as a parameter."
    locals = locals.copy()
    kwargs_in_locals = locals['kwargs']
    del locals['kwargs']
    locals.update(kwargs_in_locals)
    if not include_self and 'self' in locals:
        del locals['self']
    if isinstance(ignore, str):
        ignore = [ignore]
    for ignored_name in ignore:
        if ignored_name in locals:
            del locals[ignored_name]
    return locals
def map(function, struct, leaf_condition=None, recursive=True, trace=False, item_condition=None, content_type=None)

Iterates over all items of the struct and maps their values according to the specified function. Preserves the hierarchical structure of struct, returning an object of the same type and leaving struct untouched.

Args

function
function mapping from leaf values to new values. If not otherwise specified, the new values will be validated before map returns. If trace=True, Trace objects will be passed instead of values. For zipped structs, multiple values or a Trace containing multiple values is passed to function.
struct
struct or leaf value
leaf_condition
optional) function that determines which structs are treated as leaves. Non-structs are always treated as leaves. Leaf structs are not iterated over but directly passed to function. (Default value = None)
recursive
If True, recursively iterates over all non-leaf sub-structs, passing only leaves to function. Otherwise only iterates over direct items of struct; all sub-structs are treated as leaves. (Default value = True)
trace
If True, passes a Trace object to function instead of the value. Traces contain additional information. (Default value = False)
item_condition
optional) ItemCondition or boolean function that filters which Items are iterated over. Excluded items are left untouched. If None, the context item condition is used (data-holding items by default).
content_type
optional) Type key to use for new Structs. Defaults to VALID. Item-specific overrides can be defined by calling Item.override using the content_type as key. Override functions must have the signature (parent_struct, value).

Returns

object of the same type and hierarchy as struct

Expand source code
def map(function, struct, leaf_condition=None, recursive=True, trace=False, item_condition=None, content_type=None):
    """
    Iterates over all items of the struct and maps their values according to the specified function.
    Preserves the hierarchical structure of struct, returning an object of the same type and leaving struct untouched.

    Args:
      function: function mapping from leaf values to new values. If not otherwise specified, the new values will be validated before map returns. If trace=True, Trace objects will be passed instead of values. For zipped structs, multiple values or a Trace containing multiple values is passed to function.
      struct: struct or leaf value
      leaf_condition: optional) function that determines which structs are treated as leaves. Non-structs are always treated as leaves. Leaf structs are not iterated over but directly passed to function. (Default value = None)
      recursive: If True, recursively iterates over all non-leaf sub-structs, passing only leaves to function. Otherwise only iterates over direct items of struct; all sub-structs are treated as leaves. (Default value = True)
      trace: If True, passes a Trace object to function instead of the value. Traces contain additional information. (Default value = False)
      item_condition: optional) ItemCondition or boolean function that filters which Items are iterated over. Excluded items are left untouched. If None, the context item condition is used (data-holding items by default).
      content_type: optional) Type key to use for new Structs. Defaults to VALID. Item-specific overrides can be defined by calling Item.override using the content_type as key. Override functions must have the signature (parent_struct, value).

    Returns:
      object of the same type and hierarchy as struct

    """
    # pylint: disable-msg = redefined-builtin
    if trace is True:
        trace = Trace(struct, None, None)
    if item_condition is None:
        item_condition = context_item_condition
    if content_type is None:
        content_type = VALID
    if not isstruct(struct, leaf_condition):
        if trace is False:
            if isinstance(struct, LeafZip):
                return function(*struct.values)
            else:
                return function(struct)
        else:
            return function(trace)
    else:
        new_values = {}
        if not recursive:
            def leaf_condition(_): return True
        for item in items(struct):
            if item_condition(item):
                old_value = item.get(struct)
                if content_type is not VALID and content_type is not INVALID and item.has_override(content_type):
                    new_value = item.get_override(content_type)(struct, old_value)
                else:
                    new_value = map(function, old_value, leaf_condition, recursive,
                                    Trace(old_value, item.name, trace) if trace is not False else False,
                                    item_condition,
                                    content_type)
                new_values[item.name] = new_value
        return copy_with(struct, new_values, change_type=content_type)
def map_item(item, function, struct, leaf_condition=None, recursive=True, content_type=None)
Expand source code
def map_item(item, function, struct, leaf_condition=None, recursive=True, content_type=None):
    assert isinstance(item, Item) or isinstance(item, str)

    def item_condition(item_):
        if isinstance(item, str):
            return item_.name == item
        else:
            return item_.name == item.name
    return map(function, struct, leaf_condition=leaf_condition, recursive=recursive, trace=False, item_condition=item_condition, content_type=content_type)
def names(struct, leaf_condition=None, full_path=True, basename=None, separator='.')
Expand source code
def names(struct, leaf_condition=None, full_path=True, basename=None, separator='.'):
    def to_name(trace):
        if not full_path:
            return trace.name if basename is None else basename + separator + trace.name
        else:
            return trace.path(separator) if basename is None else basename + separator + trace.path(separator)
    return map(to_name, struct, leaf_condition, recursive=True, trace=True, content_type=names)
def print_differences(struct1, struct2, level=0)
Expand source code
def print_differences(struct1, struct2, level=0):
    if level == 0:
        print('Comparing %s with %s' % (struct1, struct2))
    indent = '  ' * level
    if not isstruct(struct1) or not isstruct(struct2):
        if not equal(struct1, struct2):
            print(indent + 'Values not equal: "%s" and "%s".' % (struct1, struct2))
        return
    items1 = to_dict(struct1)
    items2 = to_dict(struct2)
    tested_keys = []
    for key1 in items1.keys():
        if key1 not in items2:
            print(indent + 'Item "%s" is missing from %s.' % (key1, struct2))
        else:
            if not equal(items1[key1], items2[key1]):
                print('Item "%s" differs between %s and %s.' % (key1, struct1, struct2))
            print_differences(items1[key1], items2[key1], level + 1)
        tested_keys.append(key1)
    for key2 in items2.keys():
        if key2 not in tested_keys:
            print(indent + 'Item "%s" is missing from %s.' % (key2, struct1))
def properties_dict(struct)
Expand source code
def properties_dict(struct):
    if isinstance(struct, Struct):
        return struct.__properties_dict__()
    if isinstance(struct, (list, tuple)):
        return [properties_dict(s) for s in struct]
    if isinstance(struct, np.ndarray) and struct.dtype == np.object:
        return [properties_dict(s) for s in struct]
    if isinstance(struct, dict):
        return {key: properties_dict(value) for key,value in struct.items()}
    if isinstance(struct, np.ndarray):
        struct = struct.tolist()
    try:
        json.dumps(struct)
        return struct
    except TypeError:  # not serializable
        return {'type': str(struct.__class__.__name__), 'module': str(struct.__class__.__module__)}
def shape(obj, leaf_condition=None, item_condition=None)

Maps all values of a struct to their respective dynamic shapes using math.shape(). To specify custom shapes, add an override with key struct.shape to the Item.

Args

obj
struct or leaf
leaf_condition
optional) leaf_condition passed to map() (Default value = None)
item_condition
optional) item_condition passed to map() (Default value = None)

Returns

Struct of same type holding shapes instead of data

Expand source code
def shape(obj, leaf_condition=None, item_condition=None):
    """
    Maps all values of a struct to their respective dynamic shapes using `math.shape()`.
    To specify custom shapes, add an override with key struct.shape to the Item.

    Args:
      obj: struct or leaf
      leaf_condition: optional) leaf_condition passed to `map` (Default value = None)
      item_condition: optional) item_condition passed to `map` (Default value = None)

    Returns:
      Struct of same type holding shapes instead of data

    """
    def get_shape(obj):
        try:
            return choose_backend(obj).shape(obj)
        except NoBackendFound:
            return ()
    if isinstance(obj, Struct):
        if not skip_validate():
            assert obj.content_type is VALID or obj.content_type is INVALID, "shape can only be accessed on data structs but '%s' has content type '%s'" % (type(obj).__name__, obj.content_type)
    return map(get_shape, obj, leaf_condition=leaf_condition, item_condition=item_condition, content_type=shape)
def staticshape(obj, leaf_condition=None, item_condition=None)

Maps all values of a struct to their respective static shapes using math.staticshape(). To specify custom static shapes, add an override with key struct.staticshape to the Item.

Args

obj
struct or leaf
leaf_condition
optional) leaf_condition passed to map() (Default value = None)
item_condition
optional) item_condition passed to map() (Default value = None)

Returns

Struct of same type holding shapes instead of data

Expand source code
def staticshape(obj, leaf_condition=None, item_condition=None):
    """
    Maps all values of a struct to their respective static shapes using `math.staticshape()`.
    To specify custom static shapes, add an override with key struct.staticshape to the Item.

    Args:
      obj: struct or leaf
      leaf_condition: optional) leaf_condition passed to `map` (Default value = None)
      item_condition: optional) item_condition passed to `map` (Default value = None)

    Returns:
      Struct of same type holding shapes instead of data

    """
    def get_staticshape(obj):
        try:
            return choose_backend(obj).staticshape(obj)
        except NoBackendFound:
            return ()
    if isinstance(obj, Struct):
        if not skip_validate():
            assert obj.content_type is VALID or obj.content_type is INVALID, "staticshape can only be accessed on data structs but '%s' has content type '%s'" % (type(obj).__name__, obj.content_type)
    return map(get_staticshape, obj, leaf_condition=leaf_condition, item_condition=item_condition, content_type=staticshape)
def to_dict(struct, item_condition=None)
Expand source code
def to_dict(struct, item_condition=None):
    if item_condition is None:
        item_condition = context_item_condition
    if isinstance(struct, Struct):
        return struct.__to_dict__(item_condition)
    if isinstance(struct, (list, tuple, np.ndarray)):
        return {i: struct[i] for i in range(len(struct)) if item_condition(Item(name=i, validation_function=None, is_variable=True, default_value=None, dependencies=(), holds_data=True))}
    if isinstance(struct, dict):
        return struct
    raise ValueError("Not a struct: %s" % struct)
def unflatten(flat, struct, leaf_condition=None, item_condition=None, content_type=None)

Undoes a flatten() operation, restoring the contents of a struct from a list.

Args

flat
list holding the flattened contents of a struct compatible with struct
struct
structure to restore data to
leaf_condition
optional) function that determines which structs are treated as leaves. Non-structs are always treated as leaves. (Default value = None)
item_condition
optional) ItemCondition or boolean function that filters which Items are accumulated. (Default value = None)
content_type
optional) Type key to use for new Structs. Defaults to VALID. Item-specific overrides can be defined by calling Item.override using the content_type as key. Override functions must have the signature (parent_struct, value).

Returns

struct compatible with struct holding the values from the flat list

Expand source code
def unflatten(flat, struct, leaf_condition=None, item_condition=None, content_type=None):
    """
    Undoes a `flatten` operation, restoring the contents of a struct from a list.

    Args:
      flat: list holding the flattened contents of a struct compatible with `struct`
      struct: structure to restore data to
      leaf_condition: optional) function that determines which structs are treated as leaves. Non-structs are always treated as leaves. (Default value = None)
      item_condition: optional) ItemCondition or boolean function that filters which Items are accumulated. (Default value = None)
      content_type: optional) Type key to use for new Structs. Defaults to VALID. Item-specific overrides can be defined by calling Item.override using the content_type as key. Override functions must have the signature (parent_struct, value).

    Returns:
      struct compatible with `struct` holding the values from the `flat` list

    """
    flat = list(flat)
    return map(lambda _: flat.pop(0), struct, leaf_condition=leaf_condition, item_condition=item_condition, content_type=content_type)
def unsafe()
Expand source code
def unsafe():
    warnings.warn("struct.unsafe() is deprecated. Use map() with new_type argument to avoid validation.")
    return _struct_context('unsafe')
def variable(default=None, dependencies=(), holds_data=True, **trait_kwargs)

Required decorator for data_dict of custom structs. The enclosing class must be decorated with struct.definition().

Args

default
default value passed to validation if no other value is specified
dependencies
other items (string or reference for inherited constants_dict) (Default value = ())
holds_data
determines whether the variable is considered by data-related functions (Default value = True)
**trait_kwargs
 

Returns

read-only property

Expand source code
def variable(default=None, dependencies=(), holds_data=True, **trait_kwargs):
    """
    Required decorator for data_dict of custom structs.
    The enclosing class must be decorated with struct.definition().

    Args:
      default: default value passed to validation if no other value is specified
      dependencies: other items (string or reference for inherited constants_dict) (Default value = ())
      holds_data: determines whether the variable is considered by data-related functions (Default value = True)
      **trait_kwargs: 

    Returns:
      read-only property

    """
    def decorator(validate):
        return Item(validate.__name__, validate, True, default, dependencies, holds_data, **trait_kwargs)
    return decorator
def variables(struct)
Expand source code
def variables(struct):
    return to_dict(struct, VARIABLES)
def zip(structs, leaf_condition=None, item_condition=None, zip_parents_if_incompatible=False)

Builds a single struct containing LeaefZip entries from a list of compatible structs. Passing zipped structs to 'map' will call the mapping function with the all leaves at equal positions in the structure.

Example struct.map(lambda x, y: x+y, struct.zip([{0: 'Hello'}, {0: ' World'}])) returns {0: 'Hello World'}.

Args

structs
iterable collection of structs or leaves
leaf_condition
optional) function that determines which structs are treated as leaves. Non-structs are always treated as leaves. (Default value = None)
item_condition
optional) ItemCondition or boolean function that filters which Items are zipped. Excluded items should have the same values among all structs. (Default value = None)
zip_parents_if_incompatible
If True, suppresses IncompatibleStructs errors if structs with non-matching excluded items are encountered. Instead, these structs are treated as leaves and zipped. (Default value = False)

Returns

Single struct matching the structure of any of the given structs and holding LeafZip objects as leaves for non-excluded items :raise IncompatibleStructs: If structs with non-matching excluded items are encountered and zip_parents_if_incompatible=False

Expand source code
def zip(structs, leaf_condition=None, item_condition=None, zip_parents_if_incompatible=False):
    """
    Builds a single struct containing LeaefZip entries from a list of compatible structs.
    Passing zipped structs to 'map' will call the mapping function with the all leaves at equal positions in the structure.
    
    Example `struct.map(lambda x, y: x+y, struct.zip([{0: 'Hello'}, {0: ' World'}]))` returns `{0: 'Hello World'}`.

    Args:
      structs: iterable collection of structs or leaves
      leaf_condition: optional) function that determines which structs are treated as leaves. Non-structs are always treated as leaves. (Default value = None)
      item_condition: optional) ItemCondition or boolean function that filters which Items are zipped. Excluded items should have the same values among all structs. (Default value = None)
      zip_parents_if_incompatible: If True, suppresses IncompatibleStructs errors if structs with non-matching excluded items are encountered. Instead, these structs are treated as leaves and zipped. (Default value = False)

    Returns:
      Single struct matching the structure of any of the given structs and holding LeafZip objects as leaves for non-excluded items
      :raise IncompatibleStructs: If structs with non-matching excluded items are encountered and zip_parents_if_incompatible=False

    """
    # pylint: disable-msg = redefined-builtin
    assert len(structs) > 0
    first = structs[0]
    if isstruct(first, leaf_condition):
        for struct in structs[1:]:
            if not isstruct(struct):
                if zip_parents_if_incompatible:
                    return LeafZip(structs)
                else:
                    raise IncompatibleStructs('Cannot zip %s and %s because the latter is not a struct.' % (first, struct))
            if set(to_dict(struct, item_condition=item_condition).keys()) != set(to_dict(first, item_condition=item_condition).keys()):
                if zip_parents_if_incompatible:
                    return LeafZip(structs)
                else:
                    raise IncompatibleStructs('Cannot zip %s and %s because keys vary:\n%s\n%s' % (first, struct, to_dict(first, item_condition=item_condition).keys(), to_dict(struct, item_condition=item_condition).keys()))

    if not isstruct(first, leaf_condition):
        return LeafZip(structs)

    dicts = [to_dict(struct, item_condition=item_condition) for struct in structs]
    keys = dicts[0].keys()
    new_dict = {}
    for key in keys:
        values = [d[key] for d in dicts]
        values = zip(values, leaf_condition, item_condition=item_condition, zip_parents_if_incompatible=zip_parents_if_incompatible)
        new_dict[key] = values
    return copy_with(first, new_dict, change_type=zip)

Classes

class Struct (content_type=valid, **kwargs)

Deprecated.

Base class for all custom structs. To implement a custom struct, extend this class and add the decorator @struct.definition().

Args:

Returns:

Expand source code
class Struct(object):
    """
    Deprecated.
    
    Base class for all custom structs.
    To implement a custom struct, extend this class and add the decorator @struct.definition().

    Args:

    Returns:

    """

    __items__ = None
    __traits__ = None
    __initialized_class__ = None

    def __init__(self, content_type=VALID, **kwargs):
        assert isinstance(self, Struct), 'Struct.__init__() called on %s. Maybe you forgot **' % type(self)
        assert self.__initialized_class__ == self.__class__, "Instancing %s before struct class is initialized. Maybe you forgot to decorate the class with @struct.definition()" % self.__class__.__name__
        self.__content_type__ = INVALID if content_type is VALID else content_type  # VALID, INVALID, Item for property, string for custom
        for item in self.__items__:
            if item.name not in kwargs:
                kwargs[item.name] = item.default_value
        self._set_items(**kwargs)
        for trait in self.__traits__:
            trait.endow(self)
        if content_type is not INVALID:
            self.validate()

    @derived()
    def shape(self):
        """
        Retrieves the dynamic shapes of items specified through the context (see :class:`phi.struct.item_condition.ItemCondition`).
        Shapes of sub-structs are obtained using `struct.shape` while shapes of non-structs are obtained using `math.shape()`.
        
        To override the shapes of items, use `Item.override` with key `struct.shape` instead of overriding this method.
        
        The result of `x.shape` is equivalent to calling `struct.shape(x)`.
        :return: Struct of same type holding shapes instead of data

        Args:

        Returns:

        """
        from ._struct_functions import shape
        return shape(self)

    @derived()
    def staticshape(self):
        """
        Retrieves the static shapes of items specified through the context (see :class:`phi.struct.item_condition.ItemCondition`).
        Shapes of sub-structs are obtained using `struct.staticshape` while shapes of non-structs are obtained using `math.staticshape()`.
        
        To override the static shapes of items, use `Item.override` with key `struct.staticshape` instead of overriding this method.
        
        The result of `x.staticshape` is equivalent to calling `struct.staticshape(x)`.
        :return: Struct of same type holding shapes instead of data

        Args:

        Returns:

        """
        from ._struct_functions import staticshape
        return staticshape(self)

    @derived()
    def dtype(self):
        """
        Retrieves the data types of items specified through the context (see :class:`phi.struct.item_condition.ItemCondition`).
        Data types of sub-structs are obtained using `struct.dtype` while types of non-structs are obtained using `math.dtype()`.
        
        To override the dtype of items, use `Item.override` with key `struct.dtype` instead of overriding this method.
        
        The result of `x.dtype` is equivalent to calling `struct.dtype(x)`.
        :return: Struct of same type holding data types instead of data

        Args:

        Returns:

        """
        from ._struct_functions import dtype
        return dtype(self)

    def map(self, function, leaf_condition=None, recursive=True, trace=False, item_condition=None, content_type=None):
        """
        Alias for struct.map()

        Args:
          function: 
          leaf_condition:  (Default value = None)
          recursive:  (Default value = True)
          trace:  (Default value = False)
          item_condition:  (Default value = None)
          content_type:  (Default value = None)

        Returns:

        """
        from ._struct_functions import map
        return map(function, self, leaf_condition=leaf_condition, recursive=recursive, trace=trace, item_condition=item_condition, content_type=content_type)

    def map_item(self, item, function, leaf_condition=None, recursive=True, content_type=None):
        """
        Alias for struct.map_item()

        Args:
          item: 
          function: 
          leaf_condition:  (Default value = None)
          recursive:  (Default value = True)
          content_type:  (Default value = None)

        Returns:

        """
        from ._struct_functions import map_item
        return map_item(item, function, self, leaf_condition=leaf_condition, recursive=recursive, content_type=content_type)

    def copied_with(self, change_type=None, **kwargs):
        """
        Returns a copy of this Struct with some items values changed.
        The Struct, this method is invoked on, remains unaltered.
        The returned struct will be validated unless this struct is not valid or the content_type is set to something different than VALID.

        Args:
          change_type: content type of the returned struct (Default value = None)
          kwargs: Items to change, in the form item_name=new_value.
          **kwargs: 

        Returns:
          Altered copy of this object

        """
        duplicate = copy(self)
        duplicate._set_items(**kwargs)  # pylint: disable-msg = protected-access
        target_type = change_type if change_type is not None else self.__content_type__
        if target_type is VALID and not duplicate.is_valid:
            duplicate.__content_type__ = INVALID
            duplicate.validate()
        else:
            duplicate.__content_type__ = target_type
        return duplicate

    def _set_items(self, **kwargs):
        if len(kwargs) == 0:
            return
        if self.is_valid:
            self.__content_type__ = INVALID
        for name, value in kwargs.items():
            try:
                item = getattr(self.__class__, name)
            except (KeyError, TypeError):
                raise TypeError('Struct %s has no property %s' % (self, name))
            item.set(self, value)

    def validate(self):
        """
        Performs validation on this struct if it holds data and is invalid.
        Data-holding structs should always be valid while structs holding non-data content such as shapes or data types are not regarded as valid.
        :return: True if validation was performed, False otherwise

        Args:

        Returns:

        """
        if not skip_validate() and self.__can_validate__():
            self.__validate__()
            if self.__content_type__ is INVALID:
                self.__content_type__ = VALID
            return True
        else:
            return False

    def __can_validate__(self):
        return self.__content_type__ is INVALID

    def __validate__(self):
        for trait in self.__traits__:
            trait.pre_validate_struct(self)
        for item in self.__items__:
            item.validate(self)
        for trait in self.__traits__:
            trait.post_validate_struct(self)

    @property
    def is_valid(self):
        return self.__content_type__ is VALID

    @property
    def content_type(self):
        return self.__content_type__

    def __to_dict__(self, item_condition):
        if item_condition is not None:
            return {item.name: item.get(self) for item in self.__items__ if item_condition(item)}
        else:
            return {item.name: item.get(self) for item in self.__items__}

    def __properties_dict__(self):
        result = {item.name: properties_dict(getattr(self, item.name)) for item in self.__items__ if not item.holds_data}
        for item in self.__items__:
            if isstruct(item.get(self)):
                result[item.name] = properties_dict(item.get(self))
        result['type'] = str(self.__class__.__name__)
        result['module'] = str(self.__class__.__module__)
        return result

    def __eq__(self, other):
        if type(self) != type(other):  # pylint: disable-msg = unidiomatic-typecheck
            return False
        for item in self.__items__:
            if not equal(item.get(self), item.get(other)):
                return False
        return True

    def __ne__(self, other):
        return not self == other

    def __hash__(self):
        hash_value = 0
        for attr in self.__to_dict__(None).values():
            try:
                hash_value += hash(attr)
            except TypeError:  # unhashable type
                pass
        return hash_value

    def __repr__(self):
        return "%s[%s]" % (type(self).__name__, self.content_type)

Subclasses

  • phi.physics._physics.State

Instance variables

var content_type
Expand source code
@property
def content_type(self):
    return self.__content_type__
var dtype

Retrieves the data types of items specified through the context (see :class:phi.struct.item_condition.ItemCondition). Data types of sub-structs are obtained using struct.dtype while types of non-structs are obtained using math.dtype().

To override the dtype of items, use Item.override with key struct.dtype instead of overriding this method.

The result of x.dtype is equivalent to calling struct.dtype(x). :return: Struct of same type holding data types instead of data

Args:

Returns:

Expand source code
def __get__(self, instance, owner):
    if instance is not None:
        return self.getter(instance)
    else:
        self.owner = owner
        return self
var is_valid
Expand source code
@property
def is_valid(self):
    return self.__content_type__ is VALID
var shape

Retrieves the dynamic shapes of items specified through the context (see :class:phi.struct.item_condition.ItemCondition). Shapes of sub-structs are obtained using struct.shape while shapes of non-structs are obtained using math.shape().

To override the shapes of items, use Item.override with key struct.shape instead of overriding this method.

The result of x.shape is equivalent to calling struct.shape(x). :return: Struct of same type holding shapes instead of data

Args:

Returns:

Expand source code
def __get__(self, instance, owner):
    if instance is not None:
        return self.getter(instance)
    else:
        self.owner = owner
        return self
var staticshape

Retrieves the static shapes of items specified through the context (see :class:phi.struct.item_condition.ItemCondition). Shapes of sub-structs are obtained using struct.staticshape while shapes of non-structs are obtained using math.staticshape().

To override the static shapes of items, use Item.override with key struct.staticshape instead of overriding this method.

The result of x.staticshape is equivalent to calling struct.staticshape(x). :return: Struct of same type holding shapes instead of data

Args:

Returns:

Expand source code
def __get__(self, instance, owner):
    if instance is not None:
        return self.getter(instance)
    else:
        self.owner = owner
        return self

Methods

def copied_with(self, change_type=None, **kwargs)

Returns a copy of this Struct with some items values changed. The Struct, this method is invoked on, remains unaltered. The returned struct will be validated unless this struct is not valid or the content_type is set to something different than VALID.

Args

change_type
content type of the returned struct (Default value = None)
kwargs
Items to change, in the form item_name=new_value.
**kwargs
 

Returns

Altered copy of this object

Expand source code
def copied_with(self, change_type=None, **kwargs):
    """
    Returns a copy of this Struct with some items values changed.
    The Struct, this method is invoked on, remains unaltered.
    The returned struct will be validated unless this struct is not valid or the content_type is set to something different than VALID.

    Args:
      change_type: content type of the returned struct (Default value = None)
      kwargs: Items to change, in the form item_name=new_value.
      **kwargs: 

    Returns:
      Altered copy of this object

    """
    duplicate = copy(self)
    duplicate._set_items(**kwargs)  # pylint: disable-msg = protected-access
    target_type = change_type if change_type is not None else self.__content_type__
    if target_type is VALID and not duplicate.is_valid:
        duplicate.__content_type__ = INVALID
        duplicate.validate()
    else:
        duplicate.__content_type__ = target_type
    return duplicate
def map(self, function, leaf_condition=None, recursive=True, trace=False, item_condition=None, content_type=None)

Alias for struct.map()

Args

function
 
leaf_condition
(Default value = None)
recursive
(Default value = True)
trace
(Default value = False)
item_condition
(Default value = None)
content_type
(Default value = None)

Returns:

Expand source code
def map(self, function, leaf_condition=None, recursive=True, trace=False, item_condition=None, content_type=None):
    """
    Alias for struct.map()

    Args:
      function: 
      leaf_condition:  (Default value = None)
      recursive:  (Default value = True)
      trace:  (Default value = False)
      item_condition:  (Default value = None)
      content_type:  (Default value = None)

    Returns:

    """
    from ._struct_functions import map
    return map(function, self, leaf_condition=leaf_condition, recursive=recursive, trace=trace, item_condition=item_condition, content_type=content_type)
def map_item(self, item, function, leaf_condition=None, recursive=True, content_type=None)

Alias for struct.map_item()

Args

item
 
function
 
leaf_condition
(Default value = None)
recursive
(Default value = True)
content_type
(Default value = None)

Returns:

Expand source code
def map_item(self, item, function, leaf_condition=None, recursive=True, content_type=None):
    """
    Alias for struct.map_item()

    Args:
      item: 
      function: 
      leaf_condition:  (Default value = None)
      recursive:  (Default value = True)
      content_type:  (Default value = None)

    Returns:

    """
    from ._struct_functions import map_item
    return map_item(item, function, self, leaf_condition=leaf_condition, recursive=recursive, content_type=content_type)
def validate(self)

Performs validation on this struct if it holds data and is invalid. Data-holding structs should always be valid while structs holding non-data content such as shapes or data types are not regarded as valid. :return: True if validation was performed, False otherwise

Args:

Returns:

Expand source code
def validate(self):
    """
    Performs validation on this struct if it holds data and is invalid.
    Data-holding structs should always be valid while structs holding non-data content such as shapes or data types are not regarded as valid.
    :return: True if validation was performed, False otherwise

    Args:

    Returns:

    """
    if not skip_validate() and self.__can_validate__():
        self.__validate__()
        if self.__content_type__ is INVALID:
            self.__content_type__ = VALID
        return True
    else:
        return False
class Trace (value, key, parent_trace)

Used in struct.map if trace=True. Trace objects can be used to reference a specific item of a struct or sub-struct as well as gather information about it.

Args:

Returns:

Expand source code
class Trace(object):
    """
    Used in struct.map if trace=True.
    Trace objects can be used to reference a specific item of a struct or sub-struct as well as gather information about it.

    Args:

    Returns:

    """

    def __init__(self, value, key, parent_trace):
        self.value = value
        self.key = key
        self.parent = parent_trace

    @property
    def name(self):
        if self.key is None:
            return None
        if isinstance(self.key, str):
            return self.key
        else:
            return str(self.key)

    def path(self, separator='.'):
        if self.parent is not None and self.parent.key is not None:
            return self.parent.path(separator) + separator + self.name
        else:
            return self.name if self.name is not None else ''

    def __repr__(self):
        return "%s = %s" % (self.path(), self.value)

    def find_in(self, base_struct):
        if self.parent is not None and self.parent.key is not None:
            base_struct = self.parent.find_in(base_struct)
        attrs = to_dict(base_struct, item_condition=ALL_ITEMS)
        return attrs[self.key]

Instance variables

var name
Expand source code
@property
def name(self):
    if self.key is None:
        return None
    if isinstance(self.key, str):
        return self.key
    else:
        return str(self.key)

Methods

def find_in(self, base_struct)
Expand source code
def find_in(self, base_struct):
    if self.parent is not None and self.parent.key is not None:
        base_struct = self.parent.find_in(base_struct)
    attrs = to_dict(base_struct, item_condition=ALL_ITEMS)
    return attrs[self.key]
def path(self, separator='.')
Expand source code
def path(self, separator='.'):
    if self.parent is not None and self.parent.key is not None:
        return self.parent.path(separator) + separator + self.name
    else:
        return self.name if self.name is not None else ''
class Trait (keywords=())

Traits are always handled in the order in which they were declared. Inherited traits come before newly declared structs, unless explicitly re-declared.

Args:

Returns:

Expand source code
class Trait(object):
    """
    Traits are always handled in the order in which they were declared.
    Inherited traits come before newly declared structs, unless explicitly re-declared.

    Args:

    Returns:

    """

    def __init__(self, keywords=()):
        self.keywords = keywords
        for keyword in keywords:
            assert isinstance(keyword, str)

    def check_argument(self, struct_class, item, keyword, value):
        """
        Called when a keyword of the trait is used on an item.

        Args:
          struct_class: struct class
          item: Item (usually created via @variable or @constant)
          keyword: keyword present on item and part of the trait (string)
          value: value associated with the keyword for the given item

        Returns:

        """
        pass

    def endow(self, struct):
        """
        Called on newly created Structs with this trait.
        This method is called before the first validation.

        Args:
          struct: struct instance to be endowed with the trait

        Returns:

        """
        pass

    def pre_validate_struct(self, struct):
        """
        Called before a struct instance with the trait is validated.

        Args:
          struct: struct about to be validated

        Returns:

        """
        pass

    def post_validate_struct(self, struct):
        """
        Called after a struct instance with the trait is validated.

        Args:
          struct: validated struct

        Returns:

        """
        pass

    def pre_validated(self, struct, item, value):
        """
        Processes the value of an item before the validation function is called.

        Args:
          struct: struct undergoing validation
          item: item being validated
          value: item value before validation

        Returns:
          processed item value which will be passed to the validation function

        """
        return value

    def post_validated(self, struct, item, value):
        """
        Processes the value of an item after the validation function is called.

        Args:
          struct: struct undergoing validation
          item: item being validated
          value: item value after validation

        Returns:
          processed item value which will be stored in the struct

        """
        return value

    def __eq__(self, other):
        return self.__class__ == other.__class__ if isinstance(other, Trait) else False

    def __ne__(self, other):
        return not self == other

    def __repr__(self):
        return '%s (Trait)' % self.__class__.__name__

    def __hash__(self):
        return hash(self.__class__)

Methods

def check_argument(self, struct_class, item, keyword, value)

Called when a keyword of the trait is used on an item.

Args

struct_class
struct class
item
Item (usually created via @variable or @constant)
keyword
keyword present on item and part of the trait (string)
value
value associated with the keyword for the given item

Returns:

Expand source code
def check_argument(self, struct_class, item, keyword, value):
    """
    Called when a keyword of the trait is used on an item.

    Args:
      struct_class: struct class
      item: Item (usually created via @variable or @constant)
      keyword: keyword present on item and part of the trait (string)
      value: value associated with the keyword for the given item

    Returns:

    """
    pass
def endow(self, struct)

Called on newly created Structs with this trait. This method is called before the first validation.

Args

struct
struct instance to be endowed with the trait

Returns:

Expand source code
def endow(self, struct):
    """
    Called on newly created Structs with this trait.
    This method is called before the first validation.

    Args:
      struct: struct instance to be endowed with the trait

    Returns:

    """
    pass
def post_validate_struct(self, struct)

Called after a struct instance with the trait is validated.

Args

struct
validated struct

Returns:

Expand source code
def post_validate_struct(self, struct):
    """
    Called after a struct instance with the trait is validated.

    Args:
      struct: validated struct

    Returns:

    """
    pass
def post_validated(self, struct, item, value)

Processes the value of an item after the validation function is called.

Args

struct
struct undergoing validation
item
item being validated
value
item value after validation

Returns

processed item value which will be stored in the struct

Expand source code
def post_validated(self, struct, item, value):
    """
    Processes the value of an item after the validation function is called.

    Args:
      struct: struct undergoing validation
      item: item being validated
      value: item value after validation

    Returns:
      processed item value which will be stored in the struct

    """
    return value
def pre_validate_struct(self, struct)

Called before a struct instance with the trait is validated.

Args

struct
struct about to be validated

Returns:

Expand source code
def pre_validate_struct(self, struct):
    """
    Called before a struct instance with the trait is validated.

    Args:
      struct: struct about to be validated

    Returns:

    """
    pass
def pre_validated(self, struct, item, value)

Processes the value of an item before the validation function is called.

Args

struct
struct undergoing validation
item
item being validated
value
item value before validation

Returns

processed item value which will be passed to the validation function

Expand source code
def pre_validated(self, struct, item, value):
    """
    Processes the value of an item before the validation function is called.

    Args:
      struct: struct undergoing validation
      item: item being validated
      value: item value before validation

    Returns:
      processed item value which will be passed to the validation function

    """
    return value