Szymon
Szymon

Reputation: 510

Passing 2 values into constructor where one is generated from the other in Python

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

Answers (1)

Adam Smith
Adam Smith

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

Related Questions