PyRoot: typecast histograms stored in dict()

I have the following code using PyROOT

histos = dict()
for key in list:
  obj = file.Get(key)
  if not key in histos:
    histos[key] = obj.Clone()
  else
    histos[key].Add(obj)

Add() is a function of TH1, however I get the following error if the object is retrieved from a python dict():

Traceback (most recent call last):
  File "./script.py", line 35, in <module>
    histos[key].Add(obj)
AttributeError: 'PyROOT_NoneType' object has no attribute 'Add'

How can I typecast ROOT objects retrieved from dict()

The problem is not that you need to cast the object, the problem is that file.Get will return a None type object (technically a PyROOT_NoneType object, as mentioned in the error message) if it can not find the key in the file, instead of raising an exception.

You should add a check against this, like so:

histos = dict()
for key in list:
    obj = file.Get(key)
    if not obj:
        print("Could not find", key, "in", file)
        continue
    if not key in histos:
        histos[key] = obj.Clone()
    else:
        histos[key].Add(obj)
1 Like

It seems that PyROOT_NoneType corresponds to histos[key] not obj

Sorry, I take my previous reply back. When retrieveing a non-existing object, a TObject with a nullptr is returned:

# Create an empty new file
>>> f = ROOT.TFile("foo.root", "recreate")
# Try to retrieve a non-existing object from it
>>> f.Get("bar")
<ROOT.TObject object at 0x(nil)>
# Try to clone it
>>> f.Get("bar").Clone()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ReferenceError: attempt to access a null-pointer

While the code I posted guards against this, this is not your problem here.

In other words, what is potentially wrong with this code? I basically want to hadd many root files applying a different scaling to different files:

fOut = ROOT.TFile.Open("MC_sum.root", "RECREATE")
histos = dict()
for sample in samplesList:
    fIn = ROOT.TFile.Open(sample + '.root')
    for tkey in fIn.GetListOfKeys():
        key = tkey.GetName()
        obj = fIn.Get(key)
        obj.Scale(3)
        obj.SetDirectory(0)
        if not key in histos:
            histos[key] = obj.Clone()
        else
            histos[key].Add(obj)
    fIn.Close()
for key in histos:
    fOut.cd()
    histos[key].Write()
    fOut.Close()

You should really mark the code as code. If you are posting from a browser, there is the </> sign in the toolbar. If you are posting from email or similar, wrap your code with triple back-quotes (```). This way the indentation, which is quite important in Python, is conserved.

I see two unrelated problems in your code (which looks much better now).

  1. You are missing a : in the else branch.
  2. You should only close the fOut file after your final for loop.

However, this is not your problem…
When trying your code on an example file I have lying around, it seems to work. The histograms still exist, even after closing the corresponding input file.

I get AttributeError: 'PyROOT_NoneType' each time the second input file is opened

I think looping over the next input file corrupts the previous histograms. Is it some ROOT pitfall?

What you could try to do is put the files in a list of open files and close them in a separate loop:

files = [ROOT.TFile.Open(sample + '.root') for sample in samplesList]
for fIn in files:
    # Get histograms
    for tkey in fIn.GetListOfKeys():
        key = tkey.GetName()
        obj = fIn.Get(key)
        obj.Scale(3)
        if not key in histos:
            histos[key] = obj.Clone()
        else
            histos[key].Add(obj)
for fIn in files:
    fIn.Close()

By the way I don’t see why you have the obj.SetDirectory(0) there, so I removed it in the code above.
I was also able to replicate the core of the problem:

>>> f = ROOT.TFile("bla.root")
>>> h2 = f.Get("h2")
>>> d = {}
>>> d['h2'] = h2.Clone()
>>> d
{'h2': <ROOT.TH1D object ("h2") at 0x55ca3b5766d0>}
>>> f.Close()
>>> d
{'h2': None}

But maybe the answer is the same as to this similar question, and you just have to call ROOT.TH1.AddDirectory(0):

>>> f = ROOT.TFile("bla.root")
>>> ROOT.TH1.AddDirectory(0)
>>> d = {}
>>> h2 = f.Get("h2")
>>> d['h2'] = h2.Clone()
>>> d
{'h2': <ROOT.TH1D object ("h2") at 0x55ca3b57dda0>}
>>> f.Close()
>>> d
{'h2': <ROOT.TH1D object ("h2") at 0x55ca3b57dda0>}

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.