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:
  • initval (str, bool, None) – The initial name
  • inner (bool) – Return the inner-most name of the object (or not)
  • bind (bool) – Bind the descriptor to the first name it returns
Returns:

An Autoname instance

Return type:

Autoname

__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:
  • theobject (object) – The object to which’s class the descriptor is attached to
  • val (str, bool or None) – Sets the name to depending on the type: str sets the name to this str. False or None sets the name to None. True sets the name to automatically lookup.
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:
  • iterable (iterable) – The
  • on_append (callable) – callback(list, item) for append()
  • on_remove (callable) – callback(list, item) for remove()
Returns:

An IList instance

Return type:

IList

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:
  • func (callable) – function to apply to this IList’s items
  • args (iterable) – additional arguments for func
  • kwargs (dict) – additional keyword arguments for func
Returns:

An IList instance

Return type:

IList

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]
getattr(name)[source]
Parameters:name (str) – name of the items attributes to access
Returns:An IList instance
Return type:IList
Usage:
>>> l = IList([3, 5, 4])
>>> l.getattr('__add__')(2)
[5, 7, 6]
remove(obj)[source]

Remove obj from IList

Parameters:obj (object) – object to remove from list
Returns:None
Usage:
>>> l = IList([8])
>>> l
[8]
>>> l.remove(8)
>>> l
[]
>>> l.remove(6)
Traceback (most recent call last):
...
ValueError: list.remove(x): x not in list

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]
class mtoolbox.instancelog.Object[source]

Bases: object

Class to replace (builtin) object

mtoolbox.instancelog.disable()[source]

Disable the logging of objects.

mtoolbox.instancelog.enable()[source]

Enable the logging of objects.