2

I'm using ElementTree to iterate through XML elements, and I'm appending line breaks to the every element's tail. ElementTree returns None if the element has no tail. This means that whenever there is no tail, an error is thrown whenever I try to concatenate another string to it, since you can't concatenate None and a str.

>>> a = None
>>> b = "string"
>>> a += b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +=: 'NoneType' and 'str'

What would the most compact way to account for possibility of None when concatenating a string? I'm currently using the code below, but I suspect there is a simpler, more Pythonic way to rewrite it.

if element.tail:
    element.tail += "\n"
else:
    element.tail = "\n"
Nat Riddle
  • 928
  • 1
  • 10
  • 24
  • One option: `element.tail = f'{element.tail}\n' if element.tail is not None else None` – 0x5453 May 11 '21 at 19:34
  • 5
    @0x5453 that wouldn't pass a code review with me – Peter Wood May 11 '21 at 19:34
  • 3
    The only edit I might make is to check explicitly `if element.tail is not None`, otherwise the `if/else` is pretty readable and pythonic IMO – C.Nivs May 11 '21 at 19:36
  • 1
    I dont knwo a more compact version than yours, except that it could be reduced to one line : `element.tail = element.tail + "\n" if element.tail else "\n"` – TheEagle May 11 '21 at 19:36
  • 1
    Perhaps [codereview.se] would have opinions. – Peter Wood May 11 '21 at 19:38
  • There are very helpful guys, thank you! Any more input is welcome! – Nat Riddle May 11 '21 at 19:46
  • 2
    Compact != better. Your code is pretty readable to me, and should be obviousl what it is doing to any competent programmer who isn't necessarily a Python programer, which is what you want to aim for, IMO – juanpa.arrivillaga May 11 '21 at 19:54
  • Why are you adding line breaks? For pretty-printing? – mzjn May 11 '21 at 19:54
  • @mzjn Actually yes! I'll be dealing with a batch of XML files that are all formatted randomly and inconsistently, and I need them to be formatted programmatically rather than manually in my editor. But that is not the focus of the question. – Nat Riddle May 11 '21 at 19:55
  • 2
    Then you might be interested in the `indent` function that was added in Python 3.9: https://docs.python.org/3/library/xml.etree.elementtree.html#xml.etree.ElementTree.indent – mzjn May 11 '21 at 19:56
  • @juanpa.arrivillaga Good input! That is also true. I just didn't like it taking up four lines for a seemingly basic task. – Nat Riddle May 11 '21 at 19:58
  • 2
    @NatRiddle in my experience, I never run out of lines. When I'm reading code, the limiting factor is my cognitive load. Those 4 lines require minimal cognitive load for me. – juanpa.arrivillaga May 11 '21 at 19:59
  • @mzjn Wow, thanks! This will be helpful in my use case! Like I said, though, it's not entirely about this exact use case; I am still interested in gaining answers on the real question about combining `str` with `None`. – Nat Riddle May 11 '21 at 20:01

2 Answers2

2

A short way to take care of None when concatenating a string is using a str conversion with or:

a = None
b = "string"
str(a or "") + "\n"  # --> '\n'
str(b or "") + "\n"  # --> 'string\n'
Franco Morero
  • 549
  • 5
  • 20
0

PEP-8 discourages string additions unless straightforward especially in a loop because string addition may be costly as strings are immutable.

What is recommended instead and is slightly more pythonic is to append the strings to a list and use join on it.

Combining that with @Franco Morero 's solution for adding None type.

x = []
for element in XMLTree:
    x.append((element.tail or ""))
finalstring = "\n".join(x)
Valentin Kuhn
  • 753
  • 9
  • 25
kinshukdua
  • 1,944
  • 1
  • 5
  • 15
  • Hey! Your point about string immutability is good, but this code seems more complicated and considerably less readable. – Nat Riddle May 11 '21 at 19:52
  • The only extra line is appending instead of addition and joining at the end. Yes, It might be an overkill if you're not looking to loop over hundred or thousands of items. – kinshukdua May 11 '21 at 19:59
  • 2
    you can do the same without building a list: `finalstring = "\n".join(element.tail or "" for element in XMLTree)` – Franco Morero May 11 '21 at 21:01
  • 2
    @FrancoMorero `str.join` implicitly creates a `list` from the generator as it has to first calculate the size of the new string before processing all the values. It's slightly more efficient to create the list yourself. See Raymond Hettinger (core Python developer)'s answer: https://stackoverflow.com/a/9061024/1084416 – Peter Wood May 12 '21 at 10:11