10

A list contains several NoneType elements. To skip the NoneType,

for item in list :
    if item is not None :
        fp.write(item + '\n')

#OR

for item in list : 
    try :
        fp.write(item + '\n')
    except :
        pass

Which one is better and why?

SparkAndShine
  • 17,001
  • 22
  • 90
  • 134
  • 2
    Well the second one will skip all the types that don't support concatenation with a string. – Ashwini Chaudhary Jul 23 '15 at 12:16
  • What is your end goal? – demux Jul 23 '15 at 12:17
  • You'll find some information on the subject in this answer http://stackoverflow.com/questions/3845423/remove-empty-strings-from-a-list-of-strings – demux Jul 23 '15 at 12:20
  • Is there a reason none of the answers below answer your question? – loganasherjones Jul 23 '15 at 16:31
  • Important read when thinking about ignoring exceptions: [Why is “except: pass” a bad programming practice?](https://stackoverflow.com/questions/21553327/why-is-except-pass-a-bad-programming-practice) So, as mentioned in below by Tim, one should definitely do `except TypeError: ` – Nietzsche Aug 07 '18 at 18:18

3 Answers3

8

As a general rule of thumb, you should not really be using the try: except: pattern for control flow if you can help it. There is some overhead involved with raising an exception that is unnecessary in this context. Hope this helps.

loganasherjones
  • 1,012
  • 2
  • 11
  • 19
  • 4
    I disagree. An `if` statement always takes some time, whereas a `try/except` block only causes a slowdown when the exception is triggered. So if the `None` case occurs only sporadically, `try/except` will be faster - although one should definitely do `except TypeError:` so as not to mask any other errors that might occur. – Tim Pietzcker Jul 23 '15 at 12:52
  • 1
    @TimPietzcker perhaps, but the question begins with "A list contains several `NoneType` elements" which makes me think that an element being a `NoneType` is not exceptional and thus an if is appropriate. – loganasherjones Jul 23 '15 at 16:31
5

As people mentioned in the comment the try approach is not the good way, because you might skip an element because of any other exceptions that rise in that block.

So the first option is better. Here is an alternative, where you can be sure that all elements are not None, but it will consume more memory:

for item in (element for element in list if element is not None):
    fp.write(item + '\n')

P.S. do not use built-in names as variable names, in your case - list.

bagrat
  • 7,158
  • 6
  • 29
  • 47
  • I disagree a bit with that : `for i in : if : ` is often faster than using a list comprehension inside the for. – FunkySayu Jul 23 '15 at 12:23
  • Addendum: Just remembered that `filter(None, ...)` will filter all non-truthy values, i.e. it will also skip e.g. empty lists or 0. In the end, `(x for x in list if x is not None)` was probably better, but use `()` instead of `[]` to make it a generator. – tobias_k Jul 23 '15 at 12:25
  • @FunkySayu besides the speed, it is memory inefficient as I mentioned. But when is the *often* you mentioned? – bagrat Jul 23 '15 at 12:25
  • @tobias_k the difference between `()` and `[]` is like the same as for `range` and `xrange`? – bagrat Jul 23 '15 at 12:27
  • @n9code About the speed issue, I have some different results on benchs, depending data-type used, mapping, filtering... It really depends of what you are doing, and I don't think there's a generic way to explain why this is slower / faster. I should investigate on that... – FunkySayu Jul 23 '15 at 12:31
  • @FunkySayu ok, but as far as you benched, it is **often** slower? – bagrat Jul 23 '15 at 12:35
  • @n9code I didn't test with generators (`()`), but on lists (`[]`) it's obviously slower : if you delete 3 elements on a list of 1000 elements, it's not worth at all. – FunkySayu Jul 23 '15 at 13:17
  • @n9code also, considering generators are doing a function call (`list.__iter__`), you are doing 2 `__iter__` call (one on generator, the other one on the list) instead of one, and function call are kind of slow in Python – FunkySayu Jul 23 '15 at 13:19
  • I seriously think that a simple `for e in list: if e is not None: ` is faster in a lot of cases. – FunkySayu Jul 23 '15 at 13:20
  • @FunkySayu I agree, despite the speed, I also consumes `O(n)` in the worst case, so it is just an alternative for more compact code, and non-speed-memory-critical circumstances. Agree? :) – bagrat Jul 23 '15 at 13:24
  • Yes that's it. However it just delete 1 line of code for something not that much readable. But that's the global idea. – FunkySayu Jul 23 '15 at 13:28
3

The second won't be good as it will throw an exception whenever a None type element is encountered. Exception will handled in it's own way in python .

In your case you are giving a pass , so that will be done .

A more cleanest way would be :

clean = [x for x in lis if x != None]

or As pointed in the comments you could also use is not, even if it essentially compiles to the same bytecode:

clean = [x for x in lis if x is not None]

Hope this helps . When in rome do like Romans :)

coder3521
  • 2,608
  • 1
  • 28
  • 50
  • 3
    `x != None` and `x is not None` are nearly the same thing, except that `x is not None` is the official PEP recommendation. – FunkySayu Jul 23 '15 at 12:24
  • Yes , that's what i mentioned , both complies the same byte code @FunkySayu – coder3521 Jul 23 '15 at 12:26
  • But one syntax is considered as a Warning for PEP8 and some IDE based on PEP8 (like PyCharm). Do not test if a value is None with the inequality test. – FunkySayu Jul 23 '15 at 13:22