Metaprogramming¶
Autoname¶
Access an object’s name as a property
Autoname is a data-descriptor, which automatically looks up the name under which the object on which the descriptor is accessed is known by.
Import the descriptor using from mtoolbox.autoname import Autoname
.
Example:
>>> class Object(object):
... name = Autoname()
>>> obj1 = Object()
>>> obj1.name
'obj1'
>>> obj2 = Object()
>>> obj2.name
'obj2'
By default Autoname will return the outer-most name that was defined for the object:
>>> class Object(object):
... name = Autoname()
>>> def func(anobject):
... return anobject.name
>>> o = Object()
>>> func(o)
'o'
You can change this behaviour by using the ‘inner’ keyword:
>>> class Object(object):
... name = Autoname(inner=True)
>>> o = Object()
>>> def func(anobject):
... return anobject.name
>>> func(o)
'anobject'
Note
Please be aware, that getting the inner-most name, is not what you want in most cases:
>>> class Object(object):
... name = Autoname(inner=True)
... def printname(self):
... print(self.name)
>>> o = Object()
>>> o.printname()
self
When in automatic mode (see the class documentation below) the descriptor will always return a name, that is in some callframe dictionary. If you delete a name, it will use another one, that is still in use:
>>> class Object(object):
... name = Autoname()
>>> o = Object()
>>> o.name
'o'
>>> g = o
>>> del o
>>> g.name
'g'
This can be helped a bit by using the ‘bind’ keyword argument and calling <object>.name with the name that should be used first:
>>> class Object(object):
... name = Autoname(bind=True)
>>> o = Object()
>>> o.name
'o'
>>> g = o
>>> del o
>>> g.name
'o'
Warning
Defining multiple names for an object in the same call frame (which is easily said the same level of indention in your program) will cause undetermined behaviour, depending on the Python interpreter:
>>> class Object(object):
... name = Autoname()
>>> o = Object()
>>> g = o
>>> o.name in ['o', 'g']
True
-
class
mtoolbox.autoname.
Autoname
(initval=True, inner=False, bind=False)[source]¶ Bases:
object
Create a new Autoname descriptor
Parameters: Returns: An Autoname instance
Return type: -
__get__
(theobject, objtype)[source]¶ Return the name of theobject or None
Returns: the name of the object Return type: str or None - Usage:
>>> class Object(object): ... name = Autoname() >>> obj = Object() >>> obj.name 'obj' >>> obj.name = 'another name' >>> obj.name 'another name'
-
__set__
(theobject, val)[source]¶ Set the name of the theobject
Parameters: Returns: None
Raises: TypeError if type(val) is invalid
- Usage:
>>> class Object(object): ... name = Autoname() >>> o = Object() >>> o.name = 'k' >>> o.name 'k' >>> o.name = True >>> o.name 'o' >>> o.name = False >>> str(o.name) 'None' >>> o.name = 4 Traceback (most recent call last): ... TypeError: Autoname must be set to str, bool, NoneType
-
ILists¶
Module to provide an ‘intelligent’ list class
The IList class translates attribute access to the items it holds:
l.<name> == IList([obj.<name> for obj in l])
Import the IList class using from mtoolbox.ilist import IList
.
Example
>>> l = IList([complex(3, 4), complex(6)])
>>> l.real
[3.0, 6.0]
You can also use callable attributes of your objects:
>>> l = IList([complex(3, 4), complex(6)])
>>> l
[(3+4j), (6+0j)]
>>> l.conjugate()
[(3-4j), (6-0j)]
You can add callbacks, for appending and removing objects. These callbacks must accept two positional arguments - the list and the object. The callbacks are called _after_ executing append or remove:
>>> def on_append(l, x):
... print("Adding %s to %s." % (x, l))
>>> def on_remove(l, x):
... print("Removing %s from %s." % (x, l))
>>> l = IList(on_append=on_append, on_remove=on_remove)
>>> l.append(3)
Adding 3 to [3].
>>> l.remove(3)
Removing 3 from [].
>>> def invalid_callback(l):
... print(l)
>>> l = IList(on_append=invalid_callback)
Traceback (most recent call last):
...
TypeError: on_append and on_remove must accept 2 positional arguments
>>> l = IList(on_append=3)
Traceback (most recent call last):
...
TypeError: on_append and on_remove must accept 2 positional arguments
>>> def valid_callback(l=[], x=5):
... pass
>>> def valid_callback2(l, x=5, y=3):
... pass
>>> l = IList(on_append=valid_callback, on_remove=valid_callback2)
Be aware, that only attribute names, that are not used by the list class are overwritten, so if list implemented a attribute name, you can’t use it in this way. The following code doesn’t work, because list implements ‘__add__’ (so the result is NOT [4, 5] as one could expect):
>>> l = IList([1, 2])
>>> l + 3
Traceback (most recent call last):
...
TypeError: can only concatenate list (not "int") to list
If you wish to access attributes with these names, you can use
IList.getattr()
(see method documentation).
You can also apply any function to the items of an IList by
calling IList.apply()
(see method documentation).
-
class
mtoolbox.ilist.
IList
(iterable=None, on_append=None, on_remove=None)[source]¶ Bases:
list
‘intelligent’ list object
Parameters: Returns: An
IList
instanceReturn type: Note
Both callbacks must accept two positional arguments
-
append
(obj)[source]¶ Add obj to IList
Parameters: obj (object) – object to append to list Returns: None - Usage:
>>> l = IList() >>> l [] >>> l.append(3) >>> l [3]
-
apply
(func, *args, **kwargs)[source]¶ Apply func to the items of this IList
Parameters: Returns: An
IList
instanceReturn type: - Usage:
>>> def f(x, pow=2): ... return x**pow >>> l = IList([0, 1, 2, 3, 4, 5]) >>> l.apply(f, pow=3) [0, 1, 8, 27, 64, 125]
-
Instance Logging¶
log object instantiation of (almost) all python classes
Import the module using from mtoolbox import instancelog
Note
You have to run enable()
BEFORE importing
the module, which has classes you would like to log.
The reason for this is, that the name object
from the
__builtin__
namespace has to point to the object class overwrite
of the instancelog module, when a new class is defined. For the same
reason builtin objects will never be logged.
Example:
In this example MyClass1
objects will not be logged, while
MyClass2
objects will be logged:
>>> from . import instancelog
>>> class MyClass1(object):
... def __repr__(self):
... return 'MyClass1 object'
>>> instancelog.enable()
>>> class MyClass2(object):
... def __repr__(self):
... return 'MyClass2 object'
>>> objlist = []
>>> def my_callback(obj, cls, args, kwargs):
... objlist.append(obj)
>>> instancelog.callbacks.append(my_callback)
>>> obj1 = MyClass1()
>>> obj2 = MyClass2()
>>> print(objlist)
[MyClass2 object]