Skip to content

Commit f27d777

Browse files
committed
mapattr
1 parent 64cc22f commit f27d777

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
- [listinherited.py](listinherited.py) Mix-in class that provides a formatted print() or str() (Use dir() to collect both instance attr and names inherited from its classes)
2525
- [listinherited2.py](listinherited2.py) Same as listinherited.py, but more formatted
2626
- [listtree.py](listtree.py) Trace the entire class and all its object's attrs at and above self
27+
- [mapattrs.py](mapattrs.py) Map all attributes on or inherited by an
28+
instance to the instance or class from which they are inherited.
2729

2830
---
2931

mapattrs.py

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""
2+
File: mapattrs.py (3.x + 2.x)
3+
4+
Main tool: mapattrs() map all attributes on or inherited by an
5+
instance t othe instance or class from which they are inherited.
6+
7+
Assumes dir() give all attributes of an instance. To simulate
8+
inheritance, uses eitehr the class's MRO tuple, which gives the
9+
search order for new-style classes (and all in 3.x), or a recursive
10+
traversal to infer the DFLR order of classic classes in 2.x
11+
12+
Also here: inheritance() gives version-neutral class ordering;
13+
assorted dictionary tools using 3.x/2.7 comprehensions.
14+
"""
15+
16+
import pprint
17+
18+
19+
def trace(X, label='', end='\n'):
20+
print(label + pprint.pformat(X) + end) # Print nicely
21+
22+
23+
def filterdictvals(D, V):
24+
"""
25+
dict D with entries for valeus V removed.
26+
filterdictvals(dict(a=1, b=2, c=1), 1) => {'b': 2}
27+
"""
28+
return {K: V2 for (K, V2) in D.items() if V2 != V}
29+
30+
31+
def invertdict(D):
32+
"""
33+
dict D with values changed to keys (grouped by values).
34+
Values must all be hashable to work as dict/set keys.
35+
invertdict(dict(a=1, b=2, c=1)) => {1: ['a', 'c'], 2: 'b'}
36+
"""
37+
def keysof(V):
38+
return sorted(K for K in D.keys() if D[K] == V)
39+
return {V: keysof(V) for V in set(D.values())}
40+
41+
42+
def dflr(cls):
43+
"""
44+
Classics depth-first left-to-right order of class tree at cls.
45+
Cycles not possible: Python disallows on __bases__ changes.
46+
"""
47+
here = [cls]
48+
for sup in cls.__bases:
49+
here += dflr(sup)
50+
return here
51+
52+
53+
def inheritance(instance):
54+
"""
55+
Inheritance order sequnce: new-style (MRO) or classic (DFLR)
56+
"""
57+
if (hasattr(instance.__class__, '__mro__')):
58+
return (instance,) + instance.__class__.__mro__
59+
else:
60+
return [instance] + dflr(instance.__class__)
61+
62+
63+
def mapattrs(instance, withobject=False, bysource=False):
64+
"""
65+
dict with keys giving all inherited attributes of instance,
66+
with values giving the object that each is inherited from.
67+
withobject: False=remove object built-in class attributes
68+
bysource: True=group result by objects instead of attributes.
69+
Supports classes with slot that preclude __dict__ in instance.
70+
"""
71+
attr2obj = {}
72+
inherits = inheritance(instance)
73+
for attr in dir(instance):
74+
for obj in inherits:
75+
if hasattr(obj, '__dict__') and attr in obj.__dict__:
76+
attr2obj[attr] = obj
77+
break
78+
79+
if not withobject:
80+
attr2obj = filterdictvals(attr2obj, object)
81+
82+
return attr2obj if not bysource else invertdict(attr2obj)
83+
84+
85+
if __name__ == '__main__':
86+
print('Classics classes in 2.x, new-style in 3.x')
87+
class A: attr1 = 1
88+
class B(A): attr2 = 2
89+
class C(A): attr1 = 3
90+
class D(B, C): pass
91+
I = D()
92+
print("Py=>%s" % I.attr1)
93+
trace(inheritance(I), 'INH\n')
94+
trace(mapattrs(I), 'ATTRS\n')
95+
trace(mapattrs(I, bysource=True), 'OBJS\n')
96+
97+
print('New-style classes in 2.x and 3.x')
98+
class A(object): attr1 = 1
99+
class B(A): attr2 = 2
100+
class C(A): attr1 = 3
101+
class D(B, C): pass
102+
I = D()
103+
print("Py=>%s" % I.attr1)
104+
trace(inheritance(I), 'INH\n')
105+
trace(mapattrs(I), 'ATTRS\n')
106+
trace(mapattrs(I, bysource=True), 'OBJS\n')

0 commit comments

Comments
 (0)