Mix in one or more of these classes to avoid those tedious lines of administrative code for setting attributes and getting useful representations.
If you inherit HasInitableAttributes, your should be able to obj = eval(repr(obj)) without loosing data.
enthought.traits.api.HasTraits seems to mix in well also.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | """Mix-in classes for easy attribute setting and pretty representation
>>> class T(HasInitableAttributes, HasTypedAttributes, IsCallable):
... spam = str
... def __init__(self, eggs, ham = 'ham'): pass
...
>>> t = T('bacon'); t(ham = 'eggs'); t.spam += 'sausage'; t
__main__.T('bacon', ham = 'eggs', spam = 'sausage')
>>>
"""
from inspect import getargspec
from itertools import chain
class HasInitableAttributes(object):
"""Initializes attributes automatically
>>> class T(HasInitableAttributes):
... z = 0
... def __init__(self, x, y=0, **opts): pass
...
>>> t = T(0, a = 1); t
__main__.T(0, a = 1)
>>> t.x, t.y, t.z = 1, 2, 3; t
__main__.T(1, y = 2, a = 1, z = 3)
>>>
"""
def __new__(cls, *pars, **opts):
"Initialize all attributes in the signature and any other options supplied"
try:
self = super().__new__(cls, *pars, **opts)
except:
self = super().__new__(cls)
self._argspec = names, parsname, optsname, defaults = getargspec(self.__init__)
if not defaults: defaults = []
n = len(names) - len(defaults) - 1
if n - len(pars) > 0:
_s, _to = ('s', '-%d' % (n-1)) if n - len(pars) > 1 else ('', '')
missing = "%s %s (pos %d%s)" % (_s, ", ".join(names[1:n+1]), len(pars), _to)
raise TypeError("Required argument%s not found." % missing)
for n, v in chain(zip(names[-len(defaults):], defaults), zip(names[1:], pars), opts.items()):
setattr(self, n, v)
return self
def __repr__(self):
"Show all attributes in the signature and any other public attributes that are changed"
names, parsname, optsname, defaults = self._argspec
if not defaults: defaults = []
optnames = names[-len(defaults):] if defaults else []
optvalues = (getattr(self, name) for name in optnames)
othernames = sorted(set((n for n in self.__dict__ if n[0] != '_')) - set(names))
othervalues = list((getattr(self, name, None) for name in othernames))
otherdefaults = list((getattr(self.__class__, name, None) for name in othernames))
return "%s.%s(%s)" % (self.__module__, self.__class__.__name__, ", ".join(chain(
(repr(getattr(self, name)) for name in names[1:len(names)-len(defaults)]),
("%s = %r" % (name, value) for name, value, default in zip(optnames, optvalues, defaults) if value != default),
("%s = %r" % (name, value) for name, value, default in zip(othernames, othervalues, otherdefaults) if value != default))))
class HasTypedAttributes(object):
"""Objectifies class attributes automatically
>>> class T(HasTypedAttributes):
... spam = str
... class C(HasTypedAttributes):
... eggs = list
...
>>> a, b = T(), T(); a.spam += 'ham'; a.C.eggs.append('bacon'); a.spam, b.spam, a.C.eggs, b.C.eggs
('ham', '', ['bacon'], [])
>>>
"""
def __new__(cls, *pars, **opts):
try:
self = super().__new__(cls, *pars, **opts)
except:
self = super().__new__(cls)
for name in dir(self):
if name[0] != '_':
value = getattr(self, name)
if isinstance(value, type):
setattr(self, name, value(opts.pop(name)) if name in opts else value())
if opts:
raise TypeError("__init__() got%s unexpected keyword argument%s %r" %
(" an", "", opts.keys()[0]) if len(opts) == 1 else ("", "s", opts.keys()))
return self
class IsCallable(object):
"""Update attributes by calling
>>> class T(IsCallable):
... x = 0
...
>>> t = T(); t(x=1, y=2); t.x, t.y
(1, 2)
"""
def __call__(self, *pars, **opts):
self.__dict__.update(*pars, **opts)
if __name__ == '__main__':
from doctest import testmod
testmod()
class T(HasInitableAttributes, HasTypedAttributes, IsCallable):
spam = str
t = T()
assert t.spam != str
|