patternpythonMinor
Refactor deeply nested if-else
Viewed 0 times
nestedrefactordeeplyelse
Problem
I found a code on my computer that i wrote a while ago. It is based on an exercise from O'Reilly book Programming the Semantic Web. There is a class, that stores RDF triples, that is data in the form subject-predicate-object:
Variables
```
def triples(self, (s, p, o)):
# check which terms are present
try:
if s != None:
if p != None:
# s p o
if o != None:
if o in self._spo[s][p]:
yield (s, p, o)
# s p _
else:
for ro in self._spo[s][p]:
yield (s, p, ro)
else:
# s _ o
if o != None:
for rp in self._osp[o][s]:
yield (s, rp, o)
# s _ _
else:
for rp, oset in self._spo[s].items():
for ro in oset:
yield (s, rp, ro)
else:
if p != None:
# _ p o
if o != None:
for rs in self._pos[p][o]:
yield (rs, p, o)
# _ p _
else:
for ro, sset in self._pos[p].items():
for
class SimpleGraph:
def __init__(self):
self._spo = {}
self._pos = {}
self._osp = {}
def add(self, (s, p, o)):
# implementation details
pass
def remove(self, (s, p, o)):
# implementation details
passVariables
_spo, _pos, _osp are different permutations of subject, predicate, object for performance reasons, and the underlying data structure is dictionary of dictionaries of sets like {'subject': {'predicate': set([object])}} or {'object': {'subject': set([predicate])}}. The class also has a method to yield triples that match the query in a form of a tuple. If one element of a tuple is None, it acts as a wildcard.```
def triples(self, (s, p, o)):
# check which terms are present
try:
if s != None:
if p != None:
# s p o
if o != None:
if o in self._spo[s][p]:
yield (s, p, o)
# s p _
else:
for ro in self._spo[s][p]:
yield (s, p, ro)
else:
# s _ o
if o != None:
for rp in self._osp[o][s]:
yield (s, rp, o)
# s _ _
else:
for rp, oset in self._spo[s].items():
for ro in oset:
yield (s, rp, ro)
else:
if p != None:
# _ p o
if o != None:
for rs in self._pos[p][o]:
yield (rs, p, o)
# _ p _
else:
for ro, sset in self._pos[p].items():
for
Solution
-
Set
Now
-
Also, instead of using for loops you can use generator expressions to make the code more concise.
Combining this with Winston's suggestion you get the following definition
Although this is slightly longer, it is easier to maintain in the sense that if you feel that the dictionary to look at should be changed (for better performance) for some combination of input, then the modification is easily accomplished with the second definition.
Set
args = (int(s != None), int(p != None), int(o != None)) [or args = ''.join(map(str, (int(s != None), int(p != None), int(o != None))))]Now
args will be (1, 0, 1) [or '101'] if p is None and the other two aren't.-
Also, instead of using for loops you can use generator expressions to make the code more concise.
def triples(self, (s, p, o)):
try:
args = (int(s != None), int(p != None), int(o != None))
if args == (1, 1, 1):
#if o in self._spo[s][p]: #See sindikat's comment below
# yield (s, p, o)
return iter([(s, p, o)] if o in self._spo[s][p] else [])
if args == (1, 1, 0):
return ((s, p, ro) for ro in self._spo[s][p])
if args == (1, 0, 1):
return ((s, rp, o) for rp in self._osp[o][s])
if args == (1, 0, 0):
return ((s, rp, ro) for rp, oset in self._spo[s].items() for ro in oset)
if args == (0, 1, 1):
return ((rs, p, o) for rs in self._pos[p][o])
if args == (0, 1, 0):
return ((rs, p, ro) for ro, sset in self._pos[p].items() for rs in sset)
if args == (0, 0, 1):
return ((rs, rp, o) for rs, pset in self._osp[o].items() for rp in pset)
if args == (0, 0, 0):
return ((rs, rp, ro) for rs, pset in self._spo.items() for rp, oset in pset.items() for ro in oset)
except KeyError:
passCombining this with Winston's suggestion you get the following definition
def triples(self, (s, p, o)):
try:
args = (int(s != None), int(p != None), int(o != None))
if args in [(1, 1, 1), (1, 1, 0), (1, 0, 0), (0, 0, 0)]:
lookat = self._spo
a1 = s; a2 = p; a3 = o;
invperm = [0, 1, 2]
if args in [(0, 1, 1), (0, 1, 0)]:
lookat = self._pos
a1 = p; a2 = o; a3 = s;
invperm = [2, 0, 1]
if args in [(1, 0, 1), (0, 0, 1)]:
lookat = self._osp
a1 = o; a2 = s; a3 = p;
invperm = [1, 2, 0]
permute = lambda x, p: (x[p[0]], x[p[1]], x[p[2]])
if sum(args) == 3:
#if a3 in lookat[a1][a2]: #See sindikat's comment below
# yield permute((a1, a2, a3), invperm)
return iter([permute((a1, a2, a3), invperm)] if a3 in lookat[a1][a2] else [])
if sum(args) == 2:
return (permute((a1, a2, ra3), invperm) for ra3 in lookat[a1][a2])
if sum(args) == 1:
return (permute((a1, ra2, ra3), invperm) for ra2, a3set in lookat[a1].items() for ra3 in a3set)
if sum(args) == 0:
return (permute((a1, a2, a3), invperm) for ra1, a2set in lookat.items() for ra2, a3set in a2set.items() for ra3 in a3set)
except KeyError:
passAlthough this is slightly longer, it is easier to maintain in the sense that if you feel that the dictionary to look at should be changed (for better performance) for some combination of input, then the modification is easily accomplished with the second definition.
Code Snippets
def triples(self, (s, p, o)):
try:
args = (int(s != None), int(p != None), int(o != None))
if args == (1, 1, 1):
#if o in self._spo[s][p]: #See sindikat's comment below
# yield (s, p, o)
return iter([(s, p, o)] if o in self._spo[s][p] else [])
if args == (1, 1, 0):
return ((s, p, ro) for ro in self._spo[s][p])
if args == (1, 0, 1):
return ((s, rp, o) for rp in self._osp[o][s])
if args == (1, 0, 0):
return ((s, rp, ro) for rp, oset in self._spo[s].items() for ro in oset)
if args == (0, 1, 1):
return ((rs, p, o) for rs in self._pos[p][o])
if args == (0, 1, 0):
return ((rs, p, ro) for ro, sset in self._pos[p].items() for rs in sset)
if args == (0, 0, 1):
return ((rs, rp, o) for rs, pset in self._osp[o].items() for rp in pset)
if args == (0, 0, 0):
return ((rs, rp, ro) for rs, pset in self._spo.items() for rp, oset in pset.items() for ro in oset)
except KeyError:
passdef triples(self, (s, p, o)):
try:
args = (int(s != None), int(p != None), int(o != None))
if args in [(1, 1, 1), (1, 1, 0), (1, 0, 0), (0, 0, 0)]:
lookat = self._spo
a1 = s; a2 = p; a3 = o;
invperm = [0, 1, 2]
if args in [(0, 1, 1), (0, 1, 0)]:
lookat = self._pos
a1 = p; a2 = o; a3 = s;
invperm = [2, 0, 1]
if args in [(1, 0, 1), (0, 0, 1)]:
lookat = self._osp
a1 = o; a2 = s; a3 = p;
invperm = [1, 2, 0]
permute = lambda x, p: (x[p[0]], x[p[1]], x[p[2]])
if sum(args) == 3:
#if a3 in lookat[a1][a2]: #See sindikat's comment below
# yield permute((a1, a2, a3), invperm)
return iter([permute((a1, a2, a3), invperm)] if a3 in lookat[a1][a2] else [])
if sum(args) == 2:
return (permute((a1, a2, ra3), invperm) for ra3 in lookat[a1][a2])
if sum(args) == 1:
return (permute((a1, ra2, ra3), invperm) for ra2, a3set in lookat[a1].items() for ra3 in a3set)
if sum(args) == 0:
return (permute((a1, a2, a3), invperm) for ra1, a2set in lookat.items() for ra2, a3set in a2set.items() for ra3 in a3set)
except KeyError:
passContext
StackExchange Code Review Q#19470, answer score: 4
Revisions (0)
No revisions yet.