fictive.patterns.types module¶
Common patterns for working with types
Functions
Create a subclass of |
|
Classes
Like the |
|
-
fictive.patterns.types.synthesized_type(name, instances, dict_)[source]¶ Synthesize a type from one or more instance objects
The method resolution order of the new type will be based on the left-to-right order of
instances. E.g.:import pprint from fictive.patterns.types import synthesized_type class FirstClass(object): def report(self): return ('FirstClass', ) class SecondClass(object): def report(self): return ('SecondClass', ) + super(SecondClass, self).report() first = FirstClass() second = SecondClass() synth = synthesized_type( 'synth', (second, first), {'ATTRIBUTE': 'foo'})
>>> synth().report() ('SecondClass', 'FirstClass')
>>> pprint.pprint(synth.mro()) [<class 'fictive.patterns.types.synth'>, <class 'SecondClass'>, <class 'FirstClass'>, <class 'object'>]
>>> synth.ATTRIBUTE 'foo'
>>> issubclass(synth, type(first)) True >>> issubclass(synth, type(second)) True
-
fictive.patterns.types.auto_merge(*bases, metaclass=None, **dict_)[source]¶ Create a subclass of
baseswith a single merged metaclassThis function can be used to avoid
TypeError: metaclass conflictwhen subclassing from ancestors that do not share a related metaclass. E.g.:import pprint from fictive.patterns.types import auto_merge class FirstMeta(type): pass class FirstClass(object, metaclass=FirstMeta): pass class SecondMeta(type): pass class SecondClass(object, metaclass=SecondMeta): pass class ThirdMeta(type): pass class Combined(auto_merge(SecondClass, FirstClass, metaclass=ThirdMeta)): pass
>>> pprint.pprint(Combined.mro()) [<class 'Combined'>, <class 'fictive.patterns.types.AutoMergedSubclass'>, <class 'SecondClass'>, <class 'FirstClass'>, <class 'object'>]
>>> pprint.pprint(type(Combined).__mro__) (<class 'fictive.patterns.types.AutoMergedMetaclass'>, <class 'ThirdMeta'>, <class 'SecondMeta'>, <class 'FirstMeta'>, <class 'type'>, <class 'object'>)
- Parameters
bases – any number of bases classes that will be ancestors of the new class
metaclass (type) – an optional additional metaclass to be incorporated into the new class
dict – any additional keyword arguments that should be used as class attributes for the new class
-
class
fictive.patterns.types.metaclass[source]¶ Bases:
objectdecorators for controlling metaclass attributes
Python’s type system allows modification of class behaviors by modifying the class’s metaclass. While this system is powerful and flexible, explicitly defining and assigning a custom metaclass for small modifications to the class’s behavior can be overly verbose and error prone.
As an alternative to explicitly defining and invoking a custom metaclass, this group of decorators can be used to create an “ad hoc” metaclass based on the decorated methods and attributes of the class. E.g.:
from fictive.patterns.types import metaclass @metaclass.auto class MyClass(object): @metaclass.mark def __getattribute__(cls, name): ''' obfuscate class attribute access ''' _cls_ = super(type(MyClass), cls).__getattribute__ reversed_name = name[::-1] if name == 'hidden' or reversed_name == 'hidden': return _cls_(reversed_name) return _cls_(name) @classmethod def public(cls): print(f"running {cls.__name__}.public()") @classmethod def hidden(cls): print(f"running {cls.__name__}.hidden()") return 'The Magic Words are Squeamish Ossifrage' def __init__(self, value): self.hidden = value
>>> MyClass.public() running MyClass_auto.public() >>> hasattr(MyClass, 'hidden') False >>> hasattr(MyClass, 'neddih') True >>> MyClass.neddih() running MyClass_auto.hidden() 'The Magic Words are Squeamish Ossifrage'
Note, the class’s own
__getattribute__method (used for instance attribute access) is unaffected:>>> instance = MyClass('open sesame') >>> instance.hidden 'open sesame' >>> hasattr(instance, 'neddih') False >>> del instance.hidden >>> hasattr(instance, 'neddih') False >>> hasattr(instance, 'hidden') True >>> instance.hidden() running MyClass_auto.hidden() 'The Magic Words are Squeamish Ossifrage'
-
class
MARK_WRAPPER[source]¶ Bases:
ObjectProxywrapper used to mark an attribute for the metaclass
-
move_to_metaclass(name, class_attrs, meta_attrs)[source]¶ moves the wrapped object to the metaclass
This method should define the behavior for moving an attribute from the class’s
__dict__to thedictthat will be used to create a new metaclass- Parameters
class_attrs – the
class’s__dict__meta_attrs – the
dictionarythat will be passed to the new:term:metaclass’s constructor
-
-
classmethod
auto(*args, **kwargs)[source]¶ synthesize a metaclass for the decorated class
Can be used without paramaters, or in a parameterized form specfiying which attributes of the new class that should not be copied from the original class. The default set of attributes to not copy is (
__dict__and__weakref__).
-
classmethod
mark(*args, **kwargs)[source]¶ mark an attribute as belonging to the metaclass
Can be used as a method decorator or an object wrapper (e.g., for class variables). Can be used without paramaters, or in a parameterized form specfiying a custom wrapper object. E.g.:
from fictive.patterns.types import metaclass class MyWrapper(metaclass.MARK_WRAPPER): def move_to_metaclass(self, name, class_attrs, meta_attrs): super(MyWrapper, self).move_to_metaclass( name, class_attrs, meta_attrs) reversed_name = name[::-1] meta_attrs[reversed_name] = meta_attrs.pop(name) class MyClass(object): DEFAULT = metaclass.mark('default') CUSTOM = metaclass.mark(mark_wrapper=MyWrapper)('custom') @metaclass.mark def default(cls): return 'default' @metaclass.mark(mark_wrapper=MyWrapper) def custom(cls): return 'custom'
>>> type(MyClass.DEFAULT) <class 'fictive.patterns.types.metaclass.MARK_WRAPPER'> >>> type(MyClass.CUSTOM) <class 'MyWrapper'> >>> type(MyClass.default) <class 'fictive.patterns.types.metaclass.MARK_WRAPPER'> >>> type(MyClass.CUSTOM) <class 'MyWrapper'>
When the class is decorated with
metaclass.auto, the marked attributes will be moved to the metaclass using themark_wrapper’smove_to_metaclass():>>> MyClass = metaclass.auto(MyClass) >>> MyClass.DEFAULT 'default' >>> MyClass.MOTSUC 'custom' >>> MyClass.default() 'default' >>> MyClass.motsuc() 'custom'
-
class
-
class
fictive.patterns.types.class_property[source]¶ Bases:
propertyLike the
propertydecorator, but for class variablesfrom fictive.patterns.types import metaclass, class_property @metaclass.auto class MyClass(object): @class_property def class_attribute(cls): return cls._ATTRIBUTE @class_attribute.setter def class_attribute(cls, value): cls._ATTRIBUTE = value[::-1] @class_attribute.deleter def class_attribute(cls): del cls._ATTRIBUTE
>>> hasattr(MyClass, 'class_attribute') False >>> MyClass.class_attribute = 'class attribute' >>> MyClass.class_attribute 'etubirtta ssalc'
As with other class variables, instance variables exist in a separate namespace to the
class_property:>>> instance = MyClass() >>> instance.class_attribute = 'instance attribute' >>> instance.class_attribute 'instance attribute' >>> MyClass.class_attribute 'etubirtta ssalc'
If the instance has no matching attribute in its own namespace, it will read from the class’s namespace:
>>> del instance.class_attribute >>> instance.class_attribute 'etubirtta ssalc'
NB: because “data descriptors” take precedence over instance variables (and
propertyobjects are data descriptors), aclass_propertywill take precedence over class variables defined in subclasses. If a subclass needs to override aclass_property, it must declare that override in its own metaclass.-
class
MARK_WRAPPER[source]¶ Bases:
fictive.patterns.types.metaclass.MARK_WRAPPERwrapper used to mark a
propertyfor the metaclassLeaves a “trampoline” in the class’s
__dict__-
move_to_metaclass(name, class_attrs, meta_attrs)[source]¶ moves the wrapped object to the metaclass
This method should define the behavior for moving an attribute from the class’s
__dict__to thedictthat will be used to create a new metaclass- Parameters
class_attrs – the
class’s__dict__meta_attrs – the
dictionarythat will be passed to the new:term:metaclass’s constructor
-
-
class