mixin.registry
Mixin Function Registry for AxisFuzzy core class extensions.
This module provides the infrastructure for registering and dynamically injecting
mtype-agnostic structural operations into axisfuzzy.core.fuzznums.Fuzznum
and axisfuzzy.core.fuzzarray.Fuzzarray classes, as well as the top-level
axisfuzzy namespace.
The registry system enables a clean separation between core class definitions and extended functionality, allowing for modular development while maintaining a cohesive user interface.
Architecture Overview
The mixin system follows a three-phase lifecycle:
Registration Phase: Functions are registered with metadata specifying their injection targets and exposure types using the
register_mixin()decorator.Storage Phase: The
MixinFunctionRegistrystores registered functions and their associated metadata in internal dictionaries.Injection Phase: During library initialization,
MixinFunctionRegistry.build_and_inject()dynamically attaches registered functions to target classes and the module namespace.
Key Differences from Extension System
The mixin system differs from axisfuzzy.extension in several fundamental ways:
Scope: Mixin functions are mtype-agnostic and focus on structural operations (reshape, transpose, concatenate) that work uniformly across all fuzzy number types.
Dispatch: No runtime mtype-based dispatch is needed; all functions work the same way regardless of the underlying fuzzy number type.
Use Cases: Primarily for NumPy-like array manipulation and container operations.
Injection Types
Functions can be exposed in three different ways:
'instance_function': Injected as bound methods on target classes (e.g.,my_fuzzarray.reshape(2, 3)).'top_level_function': Injected into the module namespace (e.g.,axisfuzzy.reshape(my_fuzzarray, 2, 3)).'both': Exposed as both instance methods and top-level functions.
Thread Safety
The registry is not inherently thread-safe during the registration and injection phases. However, since these operations typically occur during module initialization (import time), this is generally not a concern in practice.
See also
axisfuzzy.mixin.factoryImplementation layer for mixin operations.
axisfuzzy.mixin.registerRegistration declarations for standard operations.
axisfuzzy.extensionmtype-sensitive extension system for specialized operations.
Examples
Registering a simple mixin function:
from axisfuzzy.mixin.registry import register_mixin
@register_mixin(name='is_empty', target_classes=['Fuzzarray'],
injection_type='instance_function')
def _is_empty_impl(self):
return self.size == 0
# After library initialization:
# arr = fuzzarray([...])
# arr.is_empty() # True/False
Registering a function as both instance method and top-level function:
@register_mixin(name='normalize_shape', target_classes=['Fuzzarray'],
injection_type='both')
def _normalize_shape_impl(obj):
return obj.reshape(-1)
# After library initialization:
# arr.normalize_shape() # instance method
# axisfuzzy.normalize_shape(arr) # top-level function
Manual injection during initialization:
from axisfuzzy.mixin.registry import get_registry_mixin
from axisfuzzy.core import Fuzznum, Fuzzarray
registry = get_registry_mixin()
class_map = {'Fuzznum': Fuzznum, 'Fuzzarray': Fuzzarray}
module_globals = globals()
registry.build_and_inject(class_map, module_globals)
- class axisfuzzy.mixin.registry.MixinFunctionRegistry[source]
Bases:
objectCentral registry for mtype-agnostic functions extending AxisFuzzy core classes.
This registry manages the registration, storage, and injection of functions that provide NumPy-like structural operations for
FuzznumandFuzzarrayobjects. Unlike the extension system, mixin functions work uniformly across all fuzzy number types without requiring dispatch logic.The registry supports three injection modes: instance methods, top-level functions, or both. It ensures that the extended functionality integrates seamlessly with the existing class interfaces.
- _functions
Maps function names to their implementation callables.
- Type:
dict of str to callable
- _metadata
Maps function names to their registration metadata, including target classes and injection preferences.
- Type:
dict of str to dict
Notes
The registry is designed as a singleton accessed via
get_registry_mixin().Registration typically occurs at module import time via decorators.
Injection happens once during library initialization.
See also
register_mixinConvenience decorator for function registration.
get_registry_mixinAccess to the global registry singleton.
- build_and_inject(class_map, module_namespace)[source]
Inject all registered functions into target classes and module namespace.
This method performs the final injection phase, iterating through all registered functions and attaching them to their specified targets based on the injection metadata. It handles both instance method injection (via
setattr()on classes) and top-level function injection (via dictionary assignment on the module namespace).- Parameters:
class_map (dict of str to type) – Maps class names to actual class objects for instance method injection. Typically constructed as
{'Fuzznum': Fuzznum, 'Fuzzarray': Fuzzarray}.module_namespace (dict of str to any) – Target module’s namespace (usually
globals()of the target module) where top-level functions should be injected. Functions are added as key-value pairs to this dictionary.
Notes
Instance methods are injected using
setattr(class, name, function)Top-level functions use different strategies for ‘both’ vs ‘top_level_function’ only:
‘both’: Creates a wrapper that delegates to the instance method
‘top_level_function’: Injects the original function directly
Injection is idempotent but not thread-safe
Missing classes in
class_mapare silently ignored
Examples
Typical usage during library initialization:
from axisfuzzy.core import Fuzznum, Fuzzarray from axisfuzzy.mixin.registry import get_registry_mixin # Prepare injection targets class_map = { 'Fuzznum': Fuzznum, 'Fuzzarray': Fuzzarray } module_globals = globals() # Perform injection registry = get_registry_mixin() registry.build_and_inject(class_map, module_globals) # Now functions are available: # arr = Fuzzarray(...) # arr.reshape(2, 2) # instance method # reshape(arr, 2, 2) # top-level function
Custom class mapping:
# Only inject into Fuzzarray class_map = {'Fuzzarray': Fuzzarray} registry.build_and_inject(class_map, {})
- get_top_level_function_names()[source]
Get names of all functions registered for top-level injection.
This method scans the registry metadata and returns a list of function names that should be exposed as top-level functions. It’s useful for populating
__all__lists and documentation generation.- Returns:
Sorted list of function names that have
injection_typeof ‘top_level_function’ or ‘both’.- Return type:
list of str
Examples
registry = get_registry_mixin() # Register some functions @registry.register('func1', injection_type='instance_function', target_classes=['Fuzznum']) def f1(self): pass @registry.register('func2', injection_type='top_level_function') def f2(): pass @registry.register('func3', injection_type='both', target_classes=['Fuzzarray']) def f3(self): pass names = registry.get_top_level_function_names() print(names) # ['func2', 'func3']
Use in module
__all__definition:from axisfuzzy.mixin.registry import get_registry_mixin # Get all mixin top-level functions _mixin_functions = get_registry_mixin().get_top_level_function_names() # Combine with other exports __all__ = ['Fuzznum', 'Fuzzarray'] + _mixin_functions
- register(name, target_classes=None, injection_type='both')[source]
Decorator factory to register a function for dynamic injection.
This method provides the core registration mechanism for mixin functions. It validates registration parameters, stores the function and its metadata, and returns a decorator that can be applied to the implementation function.
- Parameters:
name (str) – Unique identifier for the function within the registry. This name will be used as the method/function name after injection.
target_classes (list of str, optional) – Class names where the function should be injected as an instance method. Required when
injection_typeis ‘instance_function’ or ‘both’. Common values are [‘Fuzznum’], [‘Fuzzarray’], or [‘Fuzznum’, ‘Fuzzarray’].injection_type ({'instance_function', 'top_level_function', 'both'}, default 'both') –
Specifies how the function should be exposed:
’instance_function’: Only as a bound method on target classes
’top_level_function’: Only in the module namespace
’both’: As both instance methods and top-level functions
- Returns:
A decorator function that accepts the implementation and registers it.
- Return type:
callable
- Raises:
ValueError –
If
injection_typeis not one of the allowed values - Iftarget_classesis None when instance injection is requested - If a function with the samenameis already registered
Examples
Register an instance-only method:
@registry.register('is_normalized', target_classes=['Fuzznum', 'Fuzzarray'], injection_type='instance_function') def _is_normalized_impl(self): # Implementation logic return True
Register a top-level-only function:
@registry.register('create_identity', injection_type='top_level_function') def _create_identity_impl(mtype='qrofn'): # Implementation logic return Fuzznum(mtype).create(md=1.0, nmd=0.0)
Register both instance method and top-level function:
@registry.register('to_list', target_classes=['Fuzzarray'], injection_type='both') def _to_list_impl(self): # Works as both arr.to_list() and axisfuzzy.to_list(arr) return list(self)
- axisfuzzy.mixin.registry.get_registry_mixin()[source]
Access the global singleton
MixinFunctionRegistryinstance.This function provides the standard entry point to the mixin registry system. It returns the same registry instance across all calls, ensuring consistent registration and injection behavior throughout the library.
- Returns:
The global singleton registry instance.
- Return type:
Notes
The registry is created once when this module is first imported and reused for all subsequent calls. This singleton pattern ensures that all registered functions are stored in the same location and available for injection.
Examples
Basic registry access:
from axisfuzzy.mixin.registry import get_registry_mixin registry = get_registry_mixin() # Use registry.register(...) to register functions
Use in registration modules:
# In axisfuzzy/mixin/register.py or similar from axisfuzzy.mixin.registry import get_registry_mixin registry = get_registry_mixin() @registry.register('my_function', target_classes=['Fuzzarray']) def _my_function_impl(self): return self.copy()
Use in library initialization:
# In axisfuzzy/__init__.py from axisfuzzy.mixin.registry import get_registry_mixin from axisfuzzy.core import Fuzznum, Fuzzarray # Inject all registered mixin functions registry = get_registry_mixin() class_map = {'Fuzznum': Fuzznum, 'Fuzzarray': Fuzzarray} registry.build_and_inject(class_map, globals())
- axisfuzzy.mixin.registry.register_mixin(name, target_classes=None, injection_type='both')[source]
Convenience decorator for registering mixin functions.
This function provides a streamlined interface to the global mixin registry, eliminating the need to explicitly access the registry instance. It’s the recommended way to register mixin functions in most scenarios.
- Parameters:
name (str) – Unique function name for registry and injection.
target_classes (list of str, optional) – Class names for instance method injection. Required for ‘instance_function’ and ‘both’ injection types.
injection_type ({'instance_function', 'top_level_function', 'both'}, default 'both') – How the function should be exposed after injection.
- Returns:
Decorator function that registers the implementation.
- Return type:
callable
- Raises:
ValueError – If parameters are invalid or if the function name is already registered.
Examples
Register an instance method:
from axisfuzzy.mixin.registry import register_mixin @register_mixin('is_square', target_classes=['Fuzzarray'], injection_type='instance_function') def _is_square_impl(self): return len(set(self.shape)) <= 1
Register a top-level function:
@register_mixin('zeros_like', injection_type='top_level_function') def _zeros_like_impl(template): # Create zero array with same shape and mtype as template return Fuzzarray(shape=template.shape, mtype=template.mtype)
Register both instance method and top-level function:
@register_mixin('flatten', target_classes=['Fuzzarray'], injection_type='both') def _flatten_impl(self): # Available as both arr.flatten() and axisfuzzy.flatten(arr) return self.reshape(-1)
See also
MixinFunctionRegistry.registerThe underlying registration method.
get_registry_mixinAccess to the global registry instance.