47

for the following code

a =func()
if a != None:
    b.append(a)

a can be assigned to None, is there a way to avoid the if statement and only use one line of code?

original problem is the following

import xml.etree.ElementTree as etree

r = etree.parse(f).getroot()
b = etree.Element('register',{})

a = r.find('tag_name') # a may get None if did not find it
if a != None:
    b.append(a)

ok, I used all the answers and got this, personally I think it's the most complex python I have ever wrote so far, lol

NS_MAP = {
    'spirit' : 'http://www.spiritconsortium.org/XMLSchema/SPIRIT/1.4',
    'app' : 'http://www.app.com/SPIRIT-app'
    }

mp=etree.Element('MemoryProperty', {'version':'alpha'})
mpt=etree.ElementTree(mp)


def copy_tags(tp, op, p, tn, ns='spirit'):
    c =  p.find('{%s}%s'%(NS_MAP[ns],tn))
    if c is not None:
        (op == '<-') and tp.append(c)
        return c    

for reg in regs:
    te = etree.Element('register',{})
    copy_tags(te,'<-',reg,'name')
    copy_tags(te,'<-',reg,'addressOffset')
    copy_tags(te,'<-',reg,'access')
    (lambda e, t: copy_tags(te,'<-',t,'usageConstraints',ns='app') if t is not None else None)(te, copy_tags(te,'|',reg,'vendorExtensions'))

    mp.append(te)

mpt.write('map_gen.xml')
Jonas
  • 121,568
  • 97
  • 310
  • 388
Jerry Gao
  • 1,389
  • 3
  • 13
  • 17
  • Something like this? `b = [x for x in funcList if x is not None]`? It's hard to tell what exactly you're trying for here. – brc Jan 11 '12 at 21:01
  • 4
    It's generally considered to be a bad idea to compare objects to None using == or !=. 'is' or 'is not' are the preferred ways to check if an object is a reference to None. (== and != are essentially co-ercing None into a boolean, numeric or string value for the comparison and can thus be semantically wrong). – Jim Dennis Jan 11 '12 at 21:03
  • b is actually a class of xml.etree.ElementTree so, I am not sure the [] notation can work. – Jerry Gao Jan 11 '12 at 21:04
  • I think he is complaining about the fact that he only wants to append the result of func() if it is not null. But, since he has to check, he has an awkward if statement there. – Donald Miner Jan 11 '12 at 21:05
  • @JerryGao: tell us exactly what are you trying. – RanRag Jan 11 '12 at 21:05
  • for reading yes, but not writting – Bite code Jan 11 '12 at 21:07
  • It is not an awkward statement. It clearly expresses the intent of the code. – japreiss Jan 11 '12 at 21:07
  • @RanRag ok, re-edit the problem. – Jerry Gao Jan 11 '12 at 21:12
  • Is there any specific reason for which you need to avoid `if condition` – RanRag Jan 11 '12 at 21:14
  • @RanRag I have 10+ tags, if I can get 3 line to 1 line, I will save 20+ lines – Jerry Gao Jan 11 '12 at 21:19
  • @orangeoctopus dude, you know me so well :) – Jerry Gao Jan 11 '12 at 21:25
  • why don't you create a function like `def myfunc(tagname,mylist): a = r.find(tagname) if a is not None: mylist.append(a)` – RanRag Jan 11 '12 at 21:27
  • if you trying to avoid the **if** statement, why don't you do something like that **a and b.append(a)** – DonCallisto Jan 11 '12 at 21:55
  • @DonCallisto: it would have to be `a is not None and b.append(a)` (yours fails with childless elements) and in any case expressions with side effects are deprecable. – John Machin Jan 12 '12 at 00:16

8 Answers8

43

If you can call func() beforehand, and you want to combine the test and assignment statements into a single statement, then you can do this, with an if-else expression:

b += [a] if a is not None else []

If a is not None, then this will add [a] to b -- essentially the same operation as b.append(a)

If a is None, then this will add [] to b, which will leave b unchanged.

This won't work unless b is a list, or at least supports "+=" in-place addition. If it doesn't -- perhaps it's some custom object, then you should be able to do this:

(b.append(a) if a is not None else None)

This is an expression, evaluated for its side effects, and then thrown away. If a is None, then the b.append(a) call will never be executed. In either case, the value of the expression is None, but we don't care about it, so it gets ignored.

Now, if you want to combine the func() call with this, then you'll have to do something different in order to avoid calling func twice. If you can use the "+=" syntax, then you can do it like this:

b += filter(None, [func()])

filter(None, <list>) returns the list with all false elements (None included, but also 0 and []) removed. This statement, then, will add either [func()] or [] to b.

[Edited]

Finally, for the worst case scenario: If you can't call func() more than once, and you can't use b += <list>, and you need to accept 0, "", [], etc, and only exclude None, and you need it all on one line, here's the ugliest line of code yet:

(lambda l, a: l.append(a) if a is not None else None)(b, func())

This is essentially @ekhumoro's solution, compressed into one line. It defines an anonymous function, calls it, discards the value, and then discards the function, all for the sake of the side effect.

Now, this is a single line, but it's certainly not easier to read or understand than the original code. If I were you, I'd stick with the original, or go with @ekhumoro's idea of just defining a helper function and using that.

Ian Clelland
  • 43,011
  • 8
  • 86
  • 87
  • Problem is, where do you call `func()`? You probably don't want to call the thing several times. I'd be really interested in seeing this solution where `a` is replaced with a function call that only gets called once (in one line). – Donald Miner Jan 11 '12 at 21:05
  • b is actually a class of xml.etree.ElementTree so, I am not sure the [] notation can work. – Jerry Gao Jan 11 '12 at 21:14
  • why not just b+=[a] if a else []...instead of the if part, you would still have to call a=func() beforehand. Sorry I guess that's the part you don't like – Matt Jan 11 '12 at 21:22
  • where would you put a = func() in your one line? – Jerry Gao Jan 11 '12 at 21:23
  • For one line I like Ian's...but it seems a bit difficult to read/overkill – Matt Jan 11 '12 at 21:28
  • As I said, neither easier to read nor understand than the original code. However, I wanted to show the flexibility of python expressions, given the possible constraints of having to avoid multiple function calls, or having to rely on function side effects. I don't recommend using these -- especially the last one, but the question was asked, and I wanted to show how it could be done. – Ian Clelland Jan 12 '12 at 00:24
  • I suppose you could use b.extend([a] if a is not None else []) --- since extend will then be appending either one or zero elements. Still has an 'if' keyword in it ... but is using the ternary expression rather than an if statement). – Jim Dennis Jan 12 '12 at 01:34
  • Use `b.append(a)`, if `a` is a string, otherwise `b += [a]` will add `a` as a list of letters to `b` – antken Aug 23 '19 at 09:01
31

python 3.8 walrus operator

if a := func(): b.append(a)
Lucas Vazquez
  • 1,456
  • 16
  • 20
6

You asked the wrong question here. The clue is in your reply to one of the comments where you say "I have 10+ tags, if I can get 3 line to 1 line, I will save 20+ lines".

So your problem actually is not that you have 3 lines of code but that you are needlessly repeating 3 lines of code over and over. You could use a function to extract the repeated lines, but it sounds like in this case you may actually want a loop:

THE_TAGS = ('tag1', 'tag2', 'and so on')
for tag in THE_TAGS:
    a = r.find(tag) # a may get None if did not find it
    if a != None:
        b.append(a)

Or if you need to append to different lists:

def extract_tag(r, tag_name, to):
    a = r.find(tag_name) # a may get None if did not find it
    if a != None:
        to.append(a)

extract_tag(r, 'tag1', b)
extract_tag(r, 'tag2', c)
Duncan
  • 92,073
  • 11
  • 122
  • 156
  • I was just wondering if I can avoid the if, but this is also good. the reason I did not use is because one of the tag had a different namespace, only one... – Jerry Gao Jan 11 '12 at 21:58
  • @JerryGao: "different namespace" should not be a problem ... `taglist = ["{ns1}tag1", "{ns2}tag2", etc]`. Please explain. – John Machin Jan 11 '12 at 22:48
  • @JohnMachin for my case it's taglist = ["{ns1}tag1", "{ns1}tag2","{ns1}tag3", ... , "{ns0}TAG"] – Jerry Gao Jan 11 '12 at 23:05
  • @JerryGao: You still haven't said what is your problem with namespaces. Why can't you use this answer (or mine)? – John Machin Jan 12 '12 at 00:09
2

Attacking your real problem, and doing it in two lines for clarity:

temp = [r.find(tag) for tag in list_of_tags]
b.extend(x for x in temp if x is not None)

Note: Element.extend is new in Python 2.7/3.2

John Machin
  • 81,303
  • 11
  • 141
  • 189
2

Short answer: Not really.

Longer answer: If you really wanted to avoid this (perhaps because you want to implement this behavior --- appending only non-None values) from several different blocks of code) then you could create a class as a proxy around the underlying b object and hide the details in its append method.

class NonNoneAppender:
    def __init__(self, obj):
        if not hasattr(obj, 'append') or not callable(obj.append):
            raise ValueError, "Object must have append method"
        self.__obj = obj
    def append(self, item):
        if item is not None:
            return self.__obj.append(item)
    def __getattr__(self, attr):
        return getattr( self.__obj, attr)      

... and then you could do something like:

b = NonNoneAppender(b)

However, I'm not sure this would make any sense at all for your code.

Jim Dennis
  • 17,054
  • 13
  • 68
  • 116
1

Presumably you're not trying to remove just a single if statement from your code...

So the obvious answer is to use a function:

import xml.etree.ElementTree as etree

def append(parent, child):
    if child is not None:
        parent.append(child)

r = etree.parse(f).getroot()
b = etree.Element('register',{})

append(b, r.find('tag_name'))
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
0

You can just add everything and remove Nones at the end with b = [a for a in b if b is not None]. Or, in your particular use case, you can do b.extend(r.findall('tag_name')[:1]). This may be a bit slower, however, as it will go through the whole tree, rather than stopping at the first instance.

Acccumulation
  • 3,491
  • 1
  • 8
  • 12
-1

b+=list(set([r.find('tag_name')])-set([None]))

But it's very ugly. A little cleaner, but also a line longer:

b.append(r.find('tag_name'))
b.remove(None)

Still not very neat though. If I were you I'd just keep that if statement.

Abhranil Das
  • 5,702
  • 6
  • 35
  • 42