{"id":7084,"date":"2025-02-11T23:08:32","date_gmt":"2025-02-11T23:08:32","guid":{"rendered":"https:\/\/algocademy.com\/blog\/remove-none-from-list-in-python-a-comprehensive-guide\/"},"modified":"2026-10-15T10:34:50","modified_gmt":"2026-10-15T10:34:50","slug":"remove-none-from-list-in-python-a-comprehensive-guide","status":"publish","type":"post","link":"https:\/\/algocademy.com\/blog\/remove-none-from-list-in-python-a-comprehensive-guide\/","title":{"rendered":"Remove None from a List in Python: 6 Ways and When to Use Each"},"content":{"rendered":"<p>The short answer: use a list comprehension that tests <code>is not None<\/code>.<\/p>\n<pre><code class=\"language-python\">items = [1, None, 2, None, 3, 4, None, 5]\nclean = [x for x in items if x is not None]\nprint(clean)\n<\/code><\/pre>\n<pre><code class=\"language-text\">[1, 2, 3, 4, 5]\n<\/code><\/pre>\n<p>That creates a new list and leaves <code>items<\/code> unchanged. The rest of this post covers the cases where you want something different: changing the list in place, removing every &#8220;empty&#8221; value, handling nested data, and avoiding the two or three bugs that catch people out.<\/p>\n<h2>Why <code>is not None<\/code> and not <code>!= None<\/code><\/h2>\n<p><code>None<\/code> is a singleton: there is exactly one <code>None<\/code> object in a Python program. The identity test <code>x is None<\/code> asks &#8220;is this that object&#8221;, which is what you mean, and it cannot be fooled by a class that overrides <code>==<\/code>. PEP 8 recommends <code>is<\/code> and <code>is not<\/code> for comparisons with <code>None<\/code>.<\/p>\n<h2>The methods<\/h2>\n<h3>1. List comprehension (default choice)<\/h3>\n<pre><code class=\"language-python\">clean = [x for x in items if x is not None]\n<\/code><\/pre>\n<p>Readable, explicit about what is being removed, and fast. Use this unless you have a specific reason not to.<\/p>\n<h3>2. <code>filter(None, items)<\/code> (fastest, but read the warning)<\/h3>\n<pre><code class=\"language-python\">clean = list(filter(None, items))\n<\/code><\/pre>\n<p>Passing <code>None<\/code> as the function means &#8220;keep items that are truthy&#8221;. That removes <code>None<\/code>, but also <code>0<\/code>, <code>0.0<\/code>, <code>False<\/code>, empty strings and empty containers:<\/p>\n<pre><code class=\"language-python\">mixed = [0, None, \"\", 1, False, [], 2, 0.0]\nprint(list(filter(None, mixed)))\nprint([x for x in mixed if x is not None])\n<\/code><\/pre>\n<pre><code class=\"language-text\">[1, 2]\n[0, '', 1, False, [], 2, 0.0]\n<\/code><\/pre>\n<p>This is the most common bug with this idiom. If the list can contain zeros or empty strings that are valid data (prices, counts, optional text), use the comprehension. Use <code>filter(None, ...)<\/code> only when you really want to drop every falsy value.<\/p>\n<h3>3. <code>filter()<\/code> with a lambda<\/h3>\n<pre><code class=\"language-python\">clean = list(filter(lambda x: x is not None, items))\n<\/code><\/pre>\n<p>Correct, but slower than the comprehension because it calls a Python function for every element (see the timings below), and it is harder to read. There is no good reason to prefer it, except that it composes well if you already have a named predicate function to pass in.<\/p>\n<h3>4. A plain for loop<\/h3>\n<pre><code class=\"language-python\">clean = []\nfor x in items:\n    if x is not None:\n        clean.append(x)\n<\/code><\/pre>\n<p>Use it when the loop body does more than filtering, for example logging each skipped value or converting the kept ones.<\/p>\n<h3>5. In place: slice assignment<\/h3>\n<p>If other code holds a reference to the same list and must see the change, rebinding a variable is not enough. Assign to the whole slice instead:<\/p>\n<pre><code class=\"language-python\">a = [1, None, 2, None, 3]\nb = a                      # another reference to the same list\na[:] = [x for x in a if x is not None]\nprint(a, b)\n<\/code><\/pre>\n<pre><code class=\"language-text\">[1, 2, 3] [1, 2, 3]\n<\/code><\/pre>\n<p>Compare with plain rebinding, where <code>b<\/code> keeps the old contents:<\/p>\n<pre><code class=\"language-python\">a = [1, None, 2]\nb = a\na = [x for x in a if x is not None]\nprint(a, b)\n<\/code><\/pre>\n<pre><code class=\"language-text\">[1, 2] [1, None, 2]\n<\/code><\/pre>\n<p>This is the usual situation with function arguments and object attributes: a function that receives a list and is supposed to clean it for its caller has to use <code>lst[:] = ...<\/code> (or return the new list and let the caller reassign).<\/p>\n<h3>6. Repeated <code>remove(None)<\/code> (avoid)<\/h3>\n<pre><code class=\"language-python\">while None in items:\n    items.remove(None)\n<\/code><\/pre>\n<p>It works, and it modifies the list in place, but each <code>None in items<\/code> check and each <code>remove<\/code> scans the list from the start. With <em>k<\/em> Nones in a list of <em>n<\/em> items that is roughly <em>n &times; k<\/em> work, so it gets slow quickly. The timings below show how slow.<\/p>\n<h2>Timings<\/h2>\n<p>I measured each approach on a list of 100,000 integers where every tenth element is <code>None<\/code>, using Python 3.12 and averaging 20 runs (your numbers will differ, the ratios are what matter):<\/p>\n<table>\n<thead>\n<tr>\n<th scope=\"col\">Method<\/th>\n<th scope=\"col\">Time<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>filter(None, items)<\/code><\/td>\n<td>0.70 ms<\/td>\n<\/tr>\n<tr>\n<td>List comprehension<\/td>\n<td>1.05 ms<\/td>\n<\/tr>\n<tr>\n<td>For loop with <code>append<\/code><\/td>\n<td>1.31 ms<\/td>\n<\/tr>\n<tr>\n<td>Slice assignment from a comprehension (on a copy)<\/td>\n<td>1.76 ms<\/td>\n<\/tr>\n<tr>\n<td><code>filter<\/code> with a lambda<\/td>\n<td>3.35 ms<\/td>\n<\/tr>\n<tr>\n<td><code>itertools.filterfalse<\/code> with a lambda<\/td>\n<td>3.14 ms<\/td>\n<\/tr>\n<tr>\n<td><code>for _ in range(items.count(None)): items.remove(None)<\/code><\/td>\n<td>3,413 ms<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The <code>remove<\/code> approach is more than 3,000 times slower on this input. On a 20,000-item list, the <code>while None in ...<\/code> loop took about 270 ms against 0.21 ms for the comprehension. For lists of a few dozen items none of this matters, so choose for readability; but the gap is why <code>remove<\/code> in a loop is worth avoiding.<\/p>\n<h2>Common mistakes<\/h2>\n<h3>Removing while iterating<\/h3>\n<pre><code class=\"language-python\">bad = [None, None, 1, None]\nfor x in bad:\n    if x is None:\n        bad.remove(x)\nprint(bad)\n<\/code><\/pre>\n<pre><code class=\"language-text\">[1, None]\n<\/code><\/pre>\n<p>Two <code>None<\/code> values are still there. When you remove an element, the items after it shift left, and the loop&#8217;s internal index then skips the element that moved into the removed slot. Never change a list&#8217;s length while looping over it. Build a new list (comprehension) or loop over a copy.<\/p>\n<h3>Forgetting that <code>filter<\/code> returns an iterator<\/h3>\n<p>In Python 3, <code>filter(...)<\/code> returns a lazy iterator, not a list. <code>len(filter(None, items))<\/code> raises a <code>TypeError<\/code>, and iterating it twice gives nothing the second time. Wrap it in <code>list()<\/code> if you need a list.<\/p>\n<h3>Treating NaN like None<\/h3>\n<p>A missing value from NumPy or pandas is often <code>float('nan')<\/code>, not <code>None<\/code>, and <code>nan<\/code> is not <code>None<\/code>:<\/p>\n<pre><code class=\"language-python\">import math\nnan = float(\"nan\")\nprint([x for x in [1.0, nan, 2.0] if x is not None])\nprint([x for x in [1.0, nan, 2.0] if not math.isnan(x)])\n<\/code><\/pre>\n<pre><code class=\"language-text\">[1.0, nan, 2.0]\n[1.0, 2.0]\n<\/code><\/pre>\n<p>(<code>math.isnan<\/code> only works on numbers, so use it when the list is numeric.)<\/p>\n<h3>Dropping None and misaligning parallel lists<\/h3>\n<p>If you have two lists matched by position, removing <code>None<\/code> from only one breaks the pairing. Filter them together:<\/p>\n<pre><code class=\"language-python\">names = [\"a\", \"b\", \"c\"]\nvals = [1, None, 3]\nprint([(n, v) for n, v in zip(names, vals) if v is not None])\n<\/code><\/pre>\n<pre><code class=\"language-text\">[('a', 1), ('c', 3)]\n<\/code><\/pre>\n<h2>Other situations<\/h2>\n<h3>Nested lists<\/h3>\n<p>A flat comprehension only looks at the top level. For arbitrarily nested lists, recurse:<\/p>\n<pre><code class=\"language-python\">def strip_none(v):\n    if isinstance(v, list):\n        return [strip_none(x) for x in v if x is not None]\n    return v\n\nprint(strip_none([1, [2, None, 3], [4, [5, None]], None, 6]))\n<\/code><\/pre>\n<pre><code class=\"language-text\">[1, [2, 3], [4, [5]], 6]\n<\/code><\/pre>\n<p>This only descends into lists. If your data can contain tuples or dicts, add cases for them.<\/p>\n<h3>Dictionaries<\/h3>\n<pre><code class=\"language-python\">d = {\"a\": 1, \"b\": None, \"c\": 0}\nprint({k: v for k, v in d.items() if v is not None})\n<\/code><\/pre>\n<pre><code class=\"language-text\">{'a': 1, 'c': 0}\n<\/code><\/pre>\n<h3>Replace instead of remove<\/h3>\n<p>Sometimes the position matters and you want a default instead of deleting the item:<\/p>\n<pre><code class=\"language-python\">print([0 if x is None else x for x in items])\n<\/code><\/pre>\n<pre><code class=\"language-text\">[1, 0, 2, 0, 3, 4, 0, 5]\n<\/code><\/pre>\n<h3>Just computing something from the values<\/h3>\n<p>You do not always need a cleaned list. A generator expression skips the Nones without building anything:<\/p>\n<pre><code class=\"language-python\">print(sum(x for x in items if x is not None))\n<\/code><\/pre>\n<pre><code class=\"language-text\">15<\/code><\/pre>\n<p>This is often why the question comes up: <code>sum([1, None])<\/code> and <code>sorted([3, None, 1])<\/code> both raise <code>TypeError<\/code>, because Python cannot add or compare <code>None<\/code> with a number.<\/p>\n<h3>pandas<\/h3>\n<p>For a pandas Series or DataFrame, use <code>dropna()<\/code> rather than converting to a list: <code>pd.Series([1, None, 3]).dropna()<\/code> keeps the values <code>1.0<\/code> and <code>3.0<\/code> (the integers become floats because pandas stores the missing value as NaN).<\/p>\n<h2>Which one should you use?<\/h2>\n<ul>\n<li><strong>Default:<\/strong> <code>[x for x in items if x is not None]<\/code>.<\/li>\n<li><strong>Must update the list that others reference:<\/strong> <code>items[:] = [x for x in items if x is not None]<\/code>.<\/li>\n<li><strong>Want to drop every falsy value on purpose:<\/strong> <code>list(filter(None, items))<\/code>.<\/li>\n<li><strong>Need to log, convert or count while filtering:<\/strong> a for loop.<\/li>\n<li><strong>Avoid:<\/strong> <code>remove(None)<\/code> in a loop, and modifying a list while iterating.<\/li>\n<\/ul>\n<p>If you are still getting comfortable with lists and loops, the AlgoCademy lessons on <a href=\"https:\/\/algocademy.com\/link\/?lang=py&amp;problem=looping-through-arrays&amp;solution=1\">looping over lists<\/a> and <a href=\"https:\/\/algocademy.com\/link\/?lang=py&amp;problem=array-pop&amp;solution=1\">removing items from a list<\/a> let you practice in the browser. The same idea of building a filtered copy shows up in the <a href=\"https:\/\/algocademy.com\/blog\/mastering-the-javascript-filter-method-a-comprehensive-guide\/\">JavaScript filter method<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Remove None from a Python list with a list comprehension, filter(), or in place. Includes timings, the 0 and empty-string trap, nested lists and common bugs.<\/p>\n","protected":false},"author":1,"featured_media":7083,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23],"tags":[],"class_list":["post-7084","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-problem-solving"],"_links":{"self":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/7084"}],"collection":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/comments?post=7084"}],"version-history":[{"count":1,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/7084\/revisions"}],"predecessor-version":[{"id":8755,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/7084\/revisions\/8755"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media\/7083"}],"wp:attachment":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media?parent=7084"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/categories?post=7084"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/tags?post=7084"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}