# Pythonic way to convert XML attribute to float, or else get None

My Python code is converting XML attributes in an ElementTree to appropriate values. How do I best convert a value in this dict to a float() if it exists, or else get None?

Best I have now is:

attr = etree_elem.attrib
...


I dislike this because it's not DRY (repeats both 'radius' and attr). I had code like this:

self.radius = float(attr.get('radius', -1))
...


But (a) that only works when I know that a particular attribute value is otherwise invalid, and (b) it's not as "clean" to me as using None to properly signify that no radius was passed.

It's a bit difficult to get None without writing an explicit conditional. The next best option may be to go with float('nan') instead of -1 to make it harder to accidentally use it as a value.

self.radius = float(etree_elem.attrib.get('radius', 'nan'))


You can use math.isfinite() to test the existence of a value. The self.radius >= 0 test would also continue to work.

Neither are that great. And they are actually doing different things. Take:

attr = {'radius': None}
# TypeError: float() argument must be a string or a number, not 'NoneType'


I assume you'd want to handle that the same way, otherwise you'd not have tried the other approach. And so you could change it to:

float(attr['radius']) if attr.get('radius') is not None else None


Again this is in no way DRY, and so I'd instead make a new function say float_nonnull. Where it's defined as:

def float_nonnone(data):
if data is None:
return None
return float(data)


Which is good until you need the same with str, int, dict, etc. But would also be WET if you pass the conversion function as an argument. Unless you use functools.partial. Which can result in:

from functools import partial

def convert_nonnull(convert, data):
if data is None:
return None
return convert(data)

float_nonnull = partial(convert_nonnull, float)

print(float_nonnull(None))          # None
print(float_nonnull(1))             # 1.0
print(float_nonnull(2.2))           # 2.2
print(convert_nonnull(float, None)) # None
print(convert_nonnull(float, 1))    # 1.0


Note: it still errors on invalid input. Like 1a.

• Thanks for the answer, and pointing out edge cases. Personally, a separate function just to massage data is sort of the opposite of elegance that I was hoping for. – Phrogz Oct 24 '16 at 16:53
• BTW, why not if data is not None:\nreturn float(data) and allow the implicit None of the function handle the other case? – Phrogz Oct 26 '16 at 21:41
• @Phrogz You can do that, but I don't like an implicit return None. And to be a hobgoblin, it goes against PEP8 too. – Peilonrayz Oct 26 '16 at 22:21