0

I am getting an error about TypeError: 'staticmethod' object is not callable. Basically, you the input is a map of and given that you provide a pair of floats (pt,eta), the code should return the Y value of the bin that the particular values fall in.

I ve tried related thread (as possible duplicates) but does not seem to be getting the answer I am looking for.

Of course, if one has any recommendations how to even improve the code, that would be welcomed of course.

import ROOT as root
import sys,math

class SFs():
    global etaBinsH
    global get_EfficiencyData
    global get_EfficiencyMC
    global eff_dataH
    global eff_mcH
    global get_ScaleFactor

    @staticmethod
    def ScaleFactor(inputRootFile) :
        #inputRootFile="Muon_IsoMu27.root"
        eff_dataH = root.std.map("string", root.TGraphAsymmErrors)()
        eff_mcH = root.std.map("string", root.TGraphAsymmErrors)()
        #std::map<std::string, root.TGraphAsymmErrors *> eff_data
        #std::map<std::string, root.TGraphAsymmErrors *> eff_mc
        EtaBins=["Lt0p9", "0p9to1p2","1p2to2p1","Gt2p1"]

        print inputRootFile
        fileIn = root.TFile(inputRootFile,"read")
        fileIn.ls()
        HistoBaseName = "ZMassEta"
        etaBinsH = fileIn.Get("etaBinsH")
        #etaLabel, GraphName
        nEtaBins = int(etaBinsH.GetNbinsX())
        eff_data= []
        eff_mc= []
        #eff_mcH =root.TGraphAsymmErrors()
        print "EtaBins...........",nEtaBins, len(EtaBins)
        for iBin in range (0, nEtaBins) :
            etaLabel = EtaBins[iBin]
            GraphName = HistoBaseName+etaLabel+"_Data"
        print GraphName,etaLabel

        eff_data.append(fileIn.Get(str(GraphName)))
        eff_dataH[etaLabel]=fileIn.Get(str(GraphName))

        GraphName = HistoBaseName+etaLabel+"_MC"
        eff_mc.append(fileIn.Get(str(GraphName)))
        eff_mcH[etaLabel]=fileIn.Get(str(GraphName))

        print eff_mcH[etaLabel].GetXaxis().GetNbins()
        print eff_mcH[etaLabel].GetX()[5]
        sff = get_ScaleFactor(46.8,2.0)
        print "SFFFFFFFFFFFFFf",sff

    @staticmethod
    def get_ScaleFactor(pt, eta) :

        efficiency_data = get_EfficiencyData(pt, eta)
        efficiency_mc = get_EfficiencyMC(pt, eta)

        if  efficiency_mc != 0. :
            SF = float(efficiency_data)/float(efficiency_mc)
        else  :
            SF=1.

        print "ScaleFactor::get_ScaleFactor(double pt, double eta) Scale Factor set to",SF,efficiency_data,efficiency_mc
        return SF


    @staticmethod
    def get_EfficiencyMC(pt, eta) :

        label = FindEtaLabel(eta,"mc")
        #label= "Lt0p9"
        binNumber = etaBinsH.GetXaxis().FindFixBin(eta)
        label = etaBinsH.GetXaxis().GetBinLabel(binNumber)
        ptbin = FindPtBin(eff_mcH, label, pt)
        Eta = math.fabs(eta)
        print "eff_mcH ==================",eff_mcH,binNumber,label,ptbin
        #ptbin=10
        if ptbin == -99 : eff =1
        else  : eff= eff_mcH[label].GetY()[ptbin-1]

        if eff > 1.  : eff = -1
        if eff < 0 : eff = 0.
        print "inside eff_mc",eff
        return eff

    @staticmethod
    def get_EfficiencyData(pt, eta) :

        label = FindEtaLabel(eta,"data")
        #label= "Lt0p9"
        binNumber = etaBinsH.GetXaxis().FindFixBin(eta)
        label = etaBinsH.GetXaxis().GetBinLabel(binNumber)
        print eff_dataH
        ptbin = FindPtBin(eff_dataH, label, pt)
        Eta = math.fabs(eta)
        fileOut=root.TFile("out.root","recreate")
        fileOut.cd()
        eff_dataH[label].Write(label)

        #ptbin=10
        if ptbin == -99 : eff =1
        else  : eff= eff_dataH[label].GetY()[ptbin-1]
        print "inside eff_data",eff

        if eff > 1.  : eff = -1
        if eff < 0 : eff = 0.
        print "inside eff_data",eff,pt,eta,label

        return eff
    @staticmethod
    def FindPtBin( eff_map, EtaLabel, Pt) :

        Npoints = eff_map[EtaLabel].GetN()
        print Npoints, "for ===============>",eff_map[EtaLabel],eff_map[EtaLabel].GetN(),EtaLabel
        #ptMAX=100
        #ptMIN=90
        ptMAX = (eff_map[EtaLabel].GetX()[Npoints-1])+(eff_map[EtaLabel].GetErrorXhigh(Npoints-1))
        ptMIN = (eff_map[EtaLabel].GetX()[0])-(eff_map[EtaLabel].GetErrorXlow(0))
        if Pt >= ptMAX : return Npoints
        elif Pt < ptMIN :
            return -99
        else : return eff_map[EtaLabel].GetXaxis().FindFixBin(Pt)


    @staticmethod
    def FindEtaLabel(Eta, Which) :

        Eta = math.fabs(Eta)
        binNumber = etaBinsH.GetXaxis().FindFixBin(Eta)
        EtaLabel = etaBinsH.GetXaxis().GetBinLabel(binNumber)

        it=-1
        if str(Which) == "data" :
            it =  eff_dataH.find(EtaLabel)

        if str(Which) == "mc" :
            it = eff_mcH.find(EtaLabel)

        return EtaLabel

sf = SFs()
sff = sf.ScaleFactor("Muon_IsoMu27.root")
Terma
  • 81
  • 8
  • What is `ROOT`? Is that a module that you have defined? – C.Nivs May 22 '19 at 18:25
  • @C.Nivs have a look here https://root.cern.ch/ , but basically is a modular software for data analysis. I think anyway, the problem is in my python, not the root objects etc – Terma May 22 '19 at 18:27
  • You have to at least show us the full error. Which staticmethod is not callable? Where are you calling it? Why are they staticmethods in the first place? Why do you have global variables inside a class? Why, in fact, do you have a class at all? – Daniel Roseman May 22 '19 at 18:28
  • `Traceback (most recent call last): File "ScaleFactor2.py", line 140, in sff = sf.ScaleFactor("Muon_IsoMu27.root") File "ScaleFactor2.py", line 47, in ScaleFactor sff = get_ScaleFactor(46.8,2.0) TypeError: 'staticmethod' object is not callable` – Terma May 22 '19 at 18:31
  • Why are you doing `global etaBinsH` etc in your class??? That makes **no sense**. Just use a module. – juanpa.arrivillaga May 22 '19 at 18:38
  • @juanpa.arrivillaga, right, what you re saying is correct. But basically, I still get errors when calling the functions like `TypeError: 'NoneType' object is not callable` – Terma May 22 '19 at 21:54

2 Answers2

2

Some examples that might be helpful to see what is going on.

Example 1

class RandomClass():
    global global_function

    @staticmethod
    def random_function(input):
        print(global_function("test"))
        return "random_function({})".format(input)

    @staticmethod
    def global_function(input):
        return "global_function({})".format(input)

rc = RandomClass()
print(rc.random_function("Input!"))

Outputs

Traceback (most recent call last):
  File "test.py", line 14, in <module>
    print(rc.random_function("Input!"))
  File "test.py", line 6, in random_function
    print(global_function("test"))
TypeError: 'staticmethod' object is not callable

Example 2

class RandomClass():
    @staticmethod
    def random_function(input):
        print(global_function("test"))
        return "random_function({})".format(input)

    @staticmethod
    def global_function(input):
        return "global_function({})".format(input)

rc = RandomClass()
print(rc.random_function("Input!"))

Output

Traceback (most recent call last):
  File "test.py", line 12, in <module>
    print(rc.random_function("Input!"))
  File "test.py", line 4, in random_function
    print(global_function("test"))
NameError: global name 'global_function' is not defined

Example 3

class RandomClass():
    @staticmethod
    def random_function(input):
        print(RandomClass.global_function("test")) # Notice change here.
        return "random_function({})".format(input)

    @staticmethod
    def global_function(input):
        return "global_function({})".format(input)

rc = RandomClass()
print(rc.random_function("Input!"))

Output

global_function(test)
random_function(Input!)

Explanation

In short, a @staticmethod cannot access functions within its this class (whether defined with this or global), and instead must initialize a new and independent class to call a function within the class it resides in (example 3). As @C.Nivs mentioned, you should perhaps look into simply not using a class.

felipe
  • 7,324
  • 2
  • 28
  • 37
  • If not using a class in the first, then how would I pass the variables defined in one function to the other ? For instance, in function `ScaleFactor(inputRootFile)` I load the map, but when actually the map > is read from the other functions, it is always empty – Terma May 22 '19 at 18:34
  • C.Nivis replied to this exact issue below. :) – felipe May 22 '19 at 18:37
2

To piggyback a bit on @Felipe's answer, by not making all of your methods static, you can eliminate the need for the global declarations to share variables around, since that's what you are doing anyways:

class SFs():
    def __init__(self):
        # initialize your global vars instead as
        # instance variables
        self.etaBinsH = None
        self.get_EfficiencyData = None
        self.get_EfficiencyMC = None
        self.eff_dataH = None
        self.get_ScaleFactor = None

    # don't make this static, then you have access to the self attributes and it makes
    # your code a bit more explicit
    def scale_factor(self, input_file):
        self.eff_dataH = root.std.map("string", root.TGraphAsymmErrors)()
        self.eff_mcH = root.std.map("string", root.TGraphAsymmErrors)()

        EtaBins = ["Lt0p9", "0p9to1p2","1p2to2p1","Gt2p1"]

        print(input_file)   # print with parentheses makes this more portable between versions

        fileIn = root.TFile(input_file, "read")
        # Now you can use this through self, which is more pythonic
        self.etaBinsH = fileIn.Get("etaBinsH")

        nEtaBins = int(self.etaBinsH.GetNbinsX())
        eff_data, eff_mc = [], []
        # rest of code

Your variables can then be shared via self, and the functions can also be accessed via self, otherwise staticmethod keeps access of self out of the function, which is why you can't call any of the other functions.

Classes are namespaces, and self allows you to tie variables to the instance-level namespace. By using global, you are trying to push those variables back to the global namespace to share them around, when really, you already have access to a namespace to share those variables in!

As a simple example:

class A:
    # here is the namespace for the *class* A
    x = 0    # x is an attribute on the class A, it is accessible on the class and instance level

    def __init__(self):
        self.y = 4    # y is now explicitly tied to an instance of A, and can be shared between *instance* methods of A

    def use_y(self):
        # because this is non-static, I have access to instance level
        # variables, this is how you share them!
        print(self.y)

        # I also have access to class-level attributes
        print(self.x)

    @staticmethod
    def use_x():
        # I don't have access to self.y, because staticmethod takes that away
        try:
           print(self.y)
        except NameError:
           print("Couldn't make this work")

        print(A.x) # have to print this as a *class-level* attribute, because self isn't defined here

a = A()

a.use_y()
# 4
# 0

a.use_x()
# Couldn't make this work
# 0
C.Nivs
  • 12,353
  • 2
  • 19
  • 44
  • 1
    I think this is what I am looking for - Will be trying this now! – Terma May 22 '19 at 18:38
  • Good stuff -- this is probably what OP is/was looking for. – felipe May 22 '19 at 18:38
  • So, basically, around the end of the first function I define, I need to call another function like `sff = self.get_ScaleFactor(46.8,2.0) def get_ScaleFactor(pt, eta) : efficiency_data = get_EfficiencyData(pt, eta) efficiency_mc = get_EfficiencyMC(pt, eta) SF = float(efficiency_data)/float(efficiency_mc) return SF ` but now I get `TypeError: 'NoneType' object is not callable`. Is this due to how I call another function from within the first function ? – Terma May 22 '19 at 21:27
  • `NoneType` means the function is not defined. If you define a non-static function inside a class, then you access it by doing `self.function_name` as the `self.` designates the Python intepreter to look inside the class (which is referred to `self` inside of itself) for the function. – felipe May 25 '19 at 18:06