You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
268 lines
20 KiB
268 lines
20 KiB
11 months ago
|
<!DOCTYPE html>
|
||
|
<html lang="en">
|
||
|
<head>
|
||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||
|
|
||
|
<!-- Enable responsiveness on mobile devices-->
|
||
|
<!-- viewport-fit=cover is to support iPhone X rounded corners and notch in landscape-->
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, viewport-fit=cover">
|
||
|
|
||
|
<title>Julio Biason .Me 4.3</title>
|
||
|
|
||
|
<!-- CSS -->
|
||
|
<link rel="stylesheet" href="https://blog.juliobiason.me/print.css" media="print">
|
||
|
<link rel="stylesheet" href="https://blog.juliobiason.me/poole.css">
|
||
|
<link rel="stylesheet" href="https://blog.juliobiason.me/hyde.css">
|
||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Sans:400,400italic,700|Abril+Fatface">
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
</head>
|
||
|
|
||
|
<body class=" ">
|
||
|
|
||
|
<div class="sidebar">
|
||
|
<div class="container sidebar-sticky">
|
||
|
<div class="sidebar-about">
|
||
|
|
||
|
<a href="https://blog.juliobiason.me"><h1>Julio Biason .Me 4.3</h1></a>
|
||
|
|
||
|
<p class="lead">Old school dev living in a 2.0 dev world</p>
|
||
|
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<ul class="sidebar-nav">
|
||
|
|
||
|
|
||
|
<li class="sidebar-nav-item"><a href="/">English</a></li>
|
||
|
|
||
|
<li class="sidebar-nav-item"><a href="/pt">Português</a></li>
|
||
|
|
||
|
<li class="sidebar-nav-item"><a href="/tags">Tags (EN)</a></li>
|
||
|
|
||
|
<li class="sidebar-nav-item"><a href="/pt/tags">Tags (PT)</a></li>
|
||
|
|
||
|
|
||
|
</ul>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
|
||
|
<div class="content container">
|
||
|
|
||
|
<div class="post">
|
||
|
<h1 class="post-title">You Don't Need range()</h1>
|
||
|
<span class="post-date">
|
||
|
2020-04-16
|
||
|
|
||
|
<a href="https://blog.juliobiason.me/tags/code/">#code</a>
|
||
|
|
||
|
<a href="https://blog.juliobiason.me/tags/python/">#python</a>
|
||
|
|
||
|
<a href="https://blog.juliobiason.me/tags/range/">#range</a>
|
||
|
|
||
|
</span>
|
||
|
<p>Beginners in Python tend to use <code>range()</code> for iterating over lists. This is
|
||
|
not really necessary.</p>
|
||
|
<span id="continue-reading"></span>
|
||
|
<p>When people start programming Python, they tend to use constructions coming
|
||
|
from other languages, so they iterate over a list with something like:</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>a_list = [</span><span style="color:#d08770;">1</span><span>, </span><span style="color:#d08770;">2</span><span>, </span><span style="color:#d08770;">3</span><span>, </span><span style="color:#d08770;">4</span><span>]
|
||
|
</span><span style="color:#b48ead;">for </span><span>i </span><span style="color:#b48ead;">in </span><span style="color:#96b5b4;">range</span><span>(</span><span style="color:#96b5b4;">len</span><span>(a_list)):
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(a_list[i])
|
||
|
</span></code></pre>
|
||
|
<p>But Python have the concept of "iterable", meaning some things can be iterated
|
||
|
over, without the need of accessing each element individually. For example,
|
||
|
our previous list can be iterated with:</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>a_list = [</span><span style="color:#d08770;">1</span><span>, </span><span style="color:#d08770;">2</span><span>, </span><span style="color:#d08770;">3</span><span>, </span><span style="color:#d08770;">4</span><span>]
|
||
|
</span><span style="color:#b48ead;">for </span><span>value </span><span style="color:#b48ead;">in </span><span>a_list:
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(value)
|
||
|
</span></code></pre>
|
||
|
<p>"For every element in <code>a_list</code>, retrieve it and name it <code>value</code>."</p>
|
||
|
<p>A lot of elements are iterable: Strings are iterable, returning every
|
||
|
character in them; dictionaries are iterable, returning every key in them;
|
||
|
sets are iterable, returning every element in them; tuples are iterable,
|
||
|
returning every value in them; generators are iterable, return the next value
|
||
|
they can produce.</p>
|
||
|
<p>But what if you need to iterate over more than one iterable at the same time?</p>
|
||
|
<h2 id="enters-zip">Enters <code>zip()</code></h2>
|
||
|
<p>That's where <code>zip()</code> comes in. <code>zip()</code> allows you to merge iterables:</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>a_list = [</span><span style="color:#d08770;">1</span><span>, </span><span style="color:#d08770;">2</span><span>, </span><span style="color:#d08770;">3</span><span>, </span><span style="color:#d08770;">4</span><span>]
|
||
|
</span><span>a_tuple = ('</span><span style="color:#a3be8c;">a</span><span>', '</span><span style="color:#a3be8c;">b</span><span>', '</span><span style="color:#a3be8c;">c</span><span>', '</span><span style="color:#a3be8c;">d</span><span>')
|
||
|
</span><span style="color:#b48ead;">for </span><span>mixed_tuple </span><span style="color:#b48ead;">in </span><span style="color:#96b5b4;">zip</span><span>(a_list, a_tuple):
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(mixed_tuple)
|
||
|
</span></code></pre>
|
||
|
<p>This code prints out:</p>
|
||
|
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>(1, 'a')
|
||
|
</span><span>(2, 'b')
|
||
|
</span><span>(3, 'c')
|
||
|
</span><span>(4, 'd')
|
||
|
</span></code></pre>
|
||
|
<p>What <code>zip()</code> does is create a tuple with the first element of the first
|
||
|
iterable and the first element of the second iterable; then the second element
|
||
|
of the first iterable and the second element of the second iterable; and so
|
||
|
on. You can put as many iterables as you want in <code>zip()</code> and it will just
|
||
|
create larger tuples for each interaction.</p>
|
||
|
<h2 id="interlude-destructuring">Interlude: Destructuring</h2>
|
||
|
<p>One of the cool things in Python is "destructuring". Destructuring
|
||
|
(de-structuring or more like "breaking apart a structure") allows one to
|
||
|
extract elements from a iterable directly.</p>
|
||
|
<p>For example, if you have a tuple with two elements:</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>a_tuple = (</span><span style="color:#d08770;">1</span><span>, </span><span style="color:#d08770;">2</span><span>)
|
||
|
</span></code></pre>
|
||
|
<p>... you'd probably take every element of it in separate variables with
|
||
|
something like</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>a = a_tuple[</span><span style="color:#d08770;">0</span><span>]
|
||
|
</span><span>b = a_tuple[</span><span style="color:#d08770;">1</span><span>]
|
||
|
</span></code></pre>
|
||
|
<p>But with destructuring, you can do this in a single pass with</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>(a, b) = a_tuple
|
||
|
</span></code></pre>
|
||
|
<p>This code and the one above it will do exactly the same thing.</p>
|
||
|
<p>But why destructuring is important if we are talking about iterating over
|
||
|
elements? 'Cause <code>for</code> also has the destructuring capabilities:</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>a_list = [</span><span style="color:#d08770;">1</span><span>, </span><span style="color:#d08770;">2</span><span>, </span><span style="color:#d08770;">3</span><span>, </span><span style="color:#d08770;">4</span><span>]
|
||
|
</span><span>a_tuple = ('</span><span style="color:#a3be8c;">b</span><span>', '</span><span style="color:#a3be8c;">c</span><span>', '</span><span style="color:#a3be8c;">d</span><span>', '</span><span style="color:#a3be8c;">f</span><span>')
|
||
|
</span><span>a_string = '</span><span style="color:#a3be8c;">aeio</span><span>'
|
||
|
</span><span>
|
||
|
</span><span style="color:#b48ead;">for </span><span>(a_number, lowercase_char, uppercase_char) </span><span style="color:#b48ead;">in </span><span style="color:#96b5b4;">zip</span><span>(a_list, a_tuple, a_string):
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(a_number)
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(lowercase_char)
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(uppercase_char)
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>()
|
||
|
</span></code></pre>
|
||
|
<div style="border:1px solid grey; margin:7px; padding: 7px">
|
||
|
<p>Remember that I said that strings are also iterables and each iteration would
|
||
|
return a character? That's it.</p>
|
||
|
|
||
|
</div>
|
||
|
<p>But what happens when one of the iterables is smaller than the other one?</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>a_short_list = [</span><span style="color:#d08770;">1</span><span>, </span><span style="color:#d08770;">2</span><span>]
|
||
|
</span><span>a_long_list [</span><span style="color:#d08770;">10</span><span>, </span><span style="color:#d08770;">20</span><span>, </span><span style="color:#d08770;">30</span><span>, </span><span style="color:#d08770;">40</span><span>, </span><span style="color:#d08770;">50</span><span>, </span><span style="color:#d08770;">60</span><span>, </span><span style="color:#d08770;">70</span><span>, </span><span style="color:#d08770;">80</span><span>, </span><span style="color:#d08770;">90</span><span>]
|
||
|
</span><span style="color:#b48ead;">for </span><span>(small, big) </span><span style="color:#b48ead;">in </span><span style="color:#96b5b4;">zip</span><span>(a_short_list, a_long_list):
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(small, big)
|
||
|
</span></code></pre>
|
||
|
<p>That will print</p>
|
||
|
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>1 10
|
||
|
</span><span>2 20
|
||
|
</span></code></pre>
|
||
|
<p><code>zip()</code> stops when the shortest iterable have no more elements. To go as far
|
||
|
as the longest iterable, you need <code>itertools.zip_longest()</code>.</p>
|
||
|
<h2 id="itertools-zip-longest"><code>itertools.zip_longest()</code></h2>
|
||
|
<p><code>zip_longest()</code>, part of the <code>itertools</code> module, will transverse the iterables
|
||
|
till every one of them have no more elements. What happens with the shortest
|
||
|
of those is that its value will be replaced with <code>None</code>. Using our previous
|
||
|
example:</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span style="color:#b48ead;">import </span><span>itertools
|
||
|
</span><span>
|
||
|
</span><span>a_short_list = [</span><span style="color:#d08770;">1</span><span>, </span><span style="color:#d08770;">2</span><span>]
|
||
|
</span><span>a_long_list [</span><span style="color:#d08770;">10</span><span>, </span><span style="color:#d08770;">20</span><span>, </span><span style="color:#d08770;">30</span><span>, </span><span style="color:#d08770;">40</span><span>, </span><span style="color:#d08770;">50</span><span>, </span><span style="color:#d08770;">60</span><span>, </span><span style="color:#d08770;">70</span><span>, </span><span style="color:#d08770;">80</span><span>, </span><span style="color:#d08770;">90</span><span>]
|
||
|
</span><span style="color:#b48ead;">for </span><span>(small, big) </span><span style="color:#b48ead;">in </span><span>itertools.</span><span style="color:#bf616a;">zip_longest</span><span>(a_short_list, a_long_list):
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(small, big)
|
||
|
</span></code></pre>
|
||
|
<p>That will print:</p>
|
||
|
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>1 10
|
||
|
</span><span>2 20
|
||
|
</span><span>None 30
|
||
|
</span><span>None 40
|
||
|
</span><span>None 50
|
||
|
</span><span>None 60
|
||
|
</span><span>None 70
|
||
|
</span><span>None 80
|
||
|
</span><span>None 90
|
||
|
</span></code></pre>
|
||
|
<h2 id="careful-with-generators">Careful with generators</h2>
|
||
|
<p>One thing you must be careful when using <code>zip()</code> and <code>zip_longest()</code> are
|
||
|
generators. Why? Because some of them have no end.</p>
|
||
|
<p>Let's take one example: <code>cycle()</code>. <code>cycle()</code>, also part of the itertools
|
||
|
module, is a generator that, on request, returns the next element of an
|
||
|
iterable but, as soon as this iterable is over, it starts over. For example
|
||
|
(and I'm tacking <code>zip()</code> around this just for the sake of staying on topic,
|
||
|
and you don't need to use <code>zip()</code> with <code>cycle()</code>):</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>a_list = [</span><span style="color:#d08770;">10</span><span>, </span><span style="color:#d08770;">20</span><span>, </span><span style="color:#d08770;">30</span><span>, </span><span style="color:#d08770;">40</span><span>, </span><span style="color:#d08770;">50</span><span>, </span><span style="color:#d08770;">60</span><span>, </span><span style="color:#d08770;">70</span><span>, </span><span style="color:#d08770;">80</span><span>, </span><span style="color:#d08770;">90</span><span>]
|
||
|
</span><span style="color:#b48ead;">for </span><span>(bullet, value) </span><span style="color:#b48ead;">in </span><span style="color:#96b5b4;">zip</span><span>(</span><span style="color:#bf616a;">cycle</span><span>(['</span><span style="color:#a3be8c;">-</span><span>', '</span><span style="color:#a3be8c;">*</span><span>', '</span><span style="color:#a3be8c;">.</span><span>']), a_list):
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(bullet, value)
|
||
|
</span></code></pre>
|
||
|
<p>That will produce:</p>
|
||
|
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>- 10
|
||
|
</span><span>* 20
|
||
|
</span><span>. 30
|
||
|
</span><span>- 40
|
||
|
</span><span>* 50
|
||
|
</span><span>. 60
|
||
|
</span><span>- 70
|
||
|
</span><span>* 80
|
||
|
</span><span>. 90
|
||
|
</span></code></pre>
|
||
|
<p>What happened here is that <code>zip()</code> took the first value of the first iterable,
|
||
|
our <code>cycle(['-', '*', '.'])</code>, which was the first value of its iterable,
|
||
|
<code>'-'</code>, and the second value of the second iterable, <code>10</code>; next iteration, the
|
||
|
second value of <code>cycle()</code> was <code>'*'</code> and the second value of <code>a_list</code> was <code>20</code>;
|
||
|
third iteration, <code>cycle()</code> returned <code>'.'</code> and <code>a_list</code> returned <code>30</code>; now, on
|
||
|
the fourth iteration, <code>cycle()</code> was asked for a value and, with its iterable
|
||
|
exhausted, it returned to the first value, returning <code>'-'</code> again.</p>
|
||
|
<p>Ok, cool?</p>
|
||
|
<p>So, what's the problem with generators?</p>
|
||
|
<p>Some generators -- like <code>cycle()</code> above -- do not have an end. If you replace
|
||
|
<code>zip()</code> with <code>zip_longest()</code> on the code above, you'll see that the code will
|
||
|
never stop. It's not every generator the can produce values continuously,
|
||
|
though, so you can mess with them with no issue.</p>
|
||
|
<div style="border:1px solid grey; margin:7px; padding: 7px">
|
||
|
<p>It's not <code>zip_longest()</code> that may have an issue. You can put two <code>cycle()</code>s in
|
||
|
a <code>zip()</code> and it will keep producing tuples with no end.</p>
|
||
|
|
||
|
</div>
|
||
|
<p>All nice and dandy, but what if I need to show the index itself?</p>
|
||
|
<h2 id="enumerate-to-the-rescue"><code>enumerate()</code> to the rescue!</h2>
|
||
|
<p>Ok, so we talked about mixing more than one iterable, but what if we need the
|
||
|
position? What if we have a list of ordered results and we need to show the
|
||
|
position itself?</p>
|
||
|
<p>Again, you may be temped to use <code>range()</code>:</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>winners = ['</span><span style="color:#a3be8c;">first place</span><span>', '</span><span style="color:#a3be8c;">second place</span><span>', '</span><span style="color:#a3be8c;">third place</span><span>', '</span><span style="color:#a3be8c;">fourth place</span><span>']
|
||
|
</span><span style="color:#b48ead;">for </span><span>pos </span><span style="color:#b48ead;">in </span><span style="color:#96b5b4;">range</span><span>(</span><span style="color:#96b5b4;">len</span><span>(winners)):
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(pos + </span><span style="color:#d08770;">1</span><span>, winners[pos].</span><span style="color:#bf616a;">capitalize</span><span>())
|
||
|
</span></code></pre>
|
||
|
<p>That will print:</p>
|
||
|
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>1 First place
|
||
|
</span><span>2 Second place
|
||
|
</span><span>3 Third place
|
||
|
</span><span>4 Fourth place
|
||
|
</span></code></pre>
|
||
|
<p>One may also try to be clever and mix our newly found knowledge about <code>zip()</code>
|
||
|
and do:</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>winners = ['</span><span style="color:#a3be8c;">first place</span><span>', '</span><span style="color:#a3be8c;">second place</span><span>', '</span><span style="color:#a3be8c;">third place</span><span>', '</span><span style="color:#a3be8c;">fourth place</span><span>']
|
||
|
</span><span style="color:#b48ead;">for </span><span>(pos, name) </span><span style="color:#b48ead;">in </span><span style="color:#96b5b4;">zip</span><span>(</span><span style="color:#96b5b4;">range</span><span>(</span><span style="color:#96b5b4;">len</span><span>(winners)), winners):
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(pos + </span><span style="color:#d08770;">1</span><span>, name.</span><span style="color:#bf616a;">capitalize</span><span>())
|
||
|
</span></code></pre>
|
||
|
<p>... which ,personally, looks even more cumbersome than the first option. But
|
||
|
Python have another generator called <code>enumerate()</code> that takes one single
|
||
|
iterable, but produces tuples with the index of it and its value:</p>
|
||
|
<pre data-lang="python" style="background-color:#2b303b;color:#c0c5ce;" class="language-python "><code class="language-python" data-lang="python"><span>winners = ['</span><span style="color:#a3be8c;">first place</span><span>', '</span><span style="color:#a3be8c;">second place</span><span>', '</span><span style="color:#a3be8c;">third place</span><span>', '</span><span style="color:#a3be8c;">fourth place</span><span>']
|
||
|
</span><span style="color:#b48ead;">for </span><span>(pos, name) </span><span style="color:#b48ead;">in </span><span style="color:#96b5b4;">enumerate</span><span>(winners):
|
||
|
</span><span> </span><span style="color:#96b5b4;">print</span><span>(pos + </span><span style="color:#d08770;">1</span><span>, name.</span><span style="color:#bf616a;">capitalize</span><span>())
|
||
|
</span></code></pre>
|
||
|
<p>Even better, <code>enumerate()</code> have an option to define with will be the value of
|
||
|
the first element, so instead of that <code>pos + 1</code> in the <code>print()</code> statement, we
|
||
|
can replace the enumerate to <code>enumerate(winners, start=1)</code> and remove the
|
||
|
addition in <code>print()</code>.</p>
|
||
|
<h2 id="conclusion">Conclusion</h2>
|
||
|
<p>Iterables is one of the powerhouses of Python, as you may have noticed in the
|
||
|
beginning with the number of things that can be iterated over. Understanding
|
||
|
those will help you write better and more concise Python code, without losing
|
||
|
meaning.</p>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
</div>
|
||
|
|
||
|
</body>
|
||
|
|
||
|
</html>
|