Reputation: 510
I have some class, lets say
NT = collections.namedtuple('NT', 'v1, v2, v3, v4')
And I have 2 functions that return some value, where one function takes the value returned by another function. A simple example:
def fun1():
return random.random()
def fun2(x):
# Takes return value of fun1
return random.random() * x
Those return values will be used for constructor's arguments v2
and v3
.
I want to create some collection of these objects (let's say a set). This can be done these ways:
s1 = set()
for _ in range(3):
f1 = fun1()
f2 = fun2(f1)
s1.add(NT(1, f1, f2, 1))
s2 = {NT(1, *(lambda x: (x, fun2(x)))(fun1()), 1) for _ in range(3)}
def fun3():
f1 = fun1()
return f1, fun2(f1)
s3 = {NT(1, *fun3(), 1) for _ in range(3)}
s4 = {NT(1, *[k for j in ((i, fun2(i)) for i in (fun1(),)) for k in j], 1) for _ in range(3)}
# chain is itertools.chain
s5 = {NT(1, *chain(*chain((i, fun2(i)) for i in (fun1(),))), 1) for _ in range(3)}
I think the first option is most readable and fourth one is least readable. Also, they are arranged from fastest to slowest.
Are there more readable ways than solutions 2, 4 and 5 that can be written within a comprehension? Are there other ways to write it?
I'm asking solely out of curiosity.
Upvotes: 0
Views: 23
Reputation: 54213
For god's sake, make a constructor!
If there's some reason you need this to be a namedtuple
instead of the more full-featured class, then a simple function will do:
def make_NT():
f1 = fun1()
f2 = fun2(f1)
return NT(1, f1, f2, 1)
but otherwise I'd rather see this be a full class
class NT(object):
def __init__(self):
self.v1 = 1
self.v2 = fun1()
self.v3 = fun2(self.v2)
self.v4 = 1
and either way, this makes the set comprehension easy.
results = {make_NT() for _ in range(3)}
# or
results = {NT() for _ in range(3)}
Note that, of the above, I would consider any approach but #1 incorrect. There's simply no reason to slap that much illegible garbage together to save 4 lines of code.
Upvotes: 3