Metaclasse para definir automaticamente __slots__ e todos os argumentos passados que iniciem com o caracter '_'
1 class AutoSlots(type):
2 def __new__(meta, name, bases, data):
3 if '__init__' in data:
4 slots = data.get('__slots__', [])
5 init = data['__init__']
6 varnames = init.func_code.co_varnames
7
8 for var in varnames:
9 if var.startswith('_'):
10 var = var[1:]
11 if var not in slots:
12 slots.append(var)
13
14 if slots:
15 data['__slots__'] = slots
16
17 cls = super(AutoSlots, meta).__new__(meta, name, bases, data)
18 super(AutoSlots, cls).__init__(cls, name, bases, data)
19
20 return cls
21
22
23 class AutoAttrs(type):
24 def __call__(cls, *args, **kwds):
25 o = object.__new__(cls, *args, **kwds)
26 if hasattr(cls, '__init__'):
27 names = cls.__init__.func_code.co_varnames
28 attrs = zip(names[1:], args)
29 for k, v in attrs:
30 if k.startswith('_'):
31 setattr(o, k[1:], v)
32
33 cls.__init__(o, *args, **kwds)
34
35 return o
36
37
38 class AutoSlotsAttrs(AutoAttrs, AutoSlots):
39 pass
40
41
42 #teste:
43
44 import unittest
45
46 class Sample(object):
47 __metaclass__ = AutoSlotsAttrs
48 def __init__(self, _a, _b, c, d=None):
49 assert self.a + self.b == c
50
51
52 class Test(unittest.TestCase):
53 def testAttributes(self):
54 o = Sample(1, 2, 3, d=4)
55
56 self.assertEqual(o.a, 1)
57 self.assertEqual(o.b, 2)
58
59 self.assert_(not hasattr(o, 'c'))
60
61 self.assert_(hasattr(o, '__slots__'))
62 self.assertRaises(AttributeError, setattr, o, 'e', 10)
63
64
65 if __name__ == '__main__':
66 unittest.main()