Winston Pan
Winston Pan

Reputation: 47

What is the purpose of using "next" to iterate over a 1-tuple?

I have a question about the Python code below, which is copied from Appendix A of paper: https://arxiv.org/abs/1305.1878.

What happens in the line M=next(XD+XD.T for XD in (self.X.dot(Derivative()),))?

Why is using the in-built function next() needed here?

class singleNeutrinoSolution(object):
    '''Most likely neutrino momentum for tt-->lepton+jets'''
    def __init(self, b , mu, #Lorentz vectors
               metX, metY,   #Momentum imbalance
               sigma2,          #Momentum imlance unc. 2X2 matrix
              mW2=mW**2,mT2=mT**2):
        self.solutionSet=nuSolutionSet(b,mu,mW2,mT2)
        S2=np.vstack([np.vstack([np.linalg.inv(sigma2),[0,0]]).T,[0,0,0]])
        V0 = np.outer([metX, metY, 0], [0, 0, 1])
        deltaNu=V0-self.solutionSet.H
        self.X=np.dot(deltaNu.T,S2).dot(deltaNu)
        M=next(XD+XD.T for XD in (self.X.dot(Derivative()),))
        solutions=intersection_ellipses(M,UnitCircle())
        self.solutions=sorted(solutions,key=self.calcX2)
    def calcX2(self,t):
        return np.dot(t,self.X).dot(t)
    @property
    def chi2(self):
        return self.calcX2(self.solutions[0])
    @property
    def nu(self):
        '''Solution for neutrino momentum'''
        return self.solutionSet.H.dot(self.solution[0])

Upvotes: 1

Views: 43

Answers (2)

a_guest
a_guest

Reputation: 36249

As the generator reveals, the result of the expression self.X.dot(Derivative()) is needed two times, XD and XD.T. So the whole next + generator expression could also be written as two statements (which is a lot clearer):

XD = self.X.dot(Derivative())
M = XD + XD.T

Perhaps the authors wanted to save one line of code, but if that results in obfuscation like the presented next approach, this is a bad idea regarding readability (lines of code is generally a bad measure for code quality).

Note that starting with Python 3.9, you could also use an assignment expression(1):

M = (XD := self.X.dot(Derivative())) + XD.T

but I don't recommend it either. The two statements version is the most readable and that counts the most (especially when published as part of an academic paper).


(1) It's not exactly the same, since the assignment expression will leak XD into the local namespace while the generator won't (as it uses its own namespace).

Upvotes: 1

mkrieger1
mkrieger1

Reputation: 23139

In this line,

M=next(XD+XD.T for XD in (self.X.dot(Derivative()),))

the author wanted to reuse the result of self.X.dot(Derivative()) in an expression as the variable XD twice.

The next function returns the first value from an iterable, which in this case is a single-iteration loop (a generator expression) over a single-item tuple,

(self.X.dot(Derivative()),)

where self.X.dot(Derivative()) is the only item. Using a loop has the side effect of assigning the name XD to the current value of the iterable.

The author could have simply written

XD = self.X.dot(Derivative())
M = XD + XD.T

but they thought they were too "clever" for this.

Upvotes: 1

Related Questions