<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Hacker Culture]]></title><description><![CDATA[A blog about technology, hacking, and more in a spirit of playfulness and exploration]]></description><link>https://www.hackerculture.com/</link><image><url>https://www.hackerculture.com/favicon.png</url><title>Hacker Culture</title><link>https://www.hackerculture.com/</link></image><generator>Ghost 5.79</generator><lastBuildDate>Fri, 23 Feb 2024 03:12:06 GMT</lastBuildDate><atom:link href="https://www.hackerculture.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Dictionaries in Python]]></title><description><![CDATA[Learn how to work with dictionaries in Python and what kind of problems we can solve with them.]]></description><link>https://www.hackerculture.com/dictionaries-in-python/</link><guid isPermaLink="false">6528653d5cb41e00010df360</guid><category><![CDATA[Beginner]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Thu, 12 Oct 2023 21:32:34 GMT</pubDate><content:encoded><![CDATA[<figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/AceHvcRGSE0?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="Dictionaries in Python - Python for Beginners #python #dictionary"></iframe></figure>]]></content:encoded></item><item><title><![CDATA[Tech Jargon – A Guide to the Tech Industry Terms]]></title><description><![CDATA[This post will focus on a few software, development, and technology-related terms and phrases that you will come across in your day to day.]]></description><link>https://www.hackerculture.com/tech-industry-terms/</link><guid isPermaLink="false">63cebb60df5713004d80dedb</guid><category><![CDATA[Beginner]]></category><category><![CDATA[Technology]]></category><category><![CDATA[Industry]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Wed, 15 Mar 2023 14:00:46 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/drew-beamer-3SIXZisims4-unsplash--1-.webp" medium="image"/><content:encoded/></item><item><title><![CDATA[Choosing the Right Data Structures in Python]]></title><description><![CDATA[Understanding and utilizing various data structures is a fundamental part of writing efficient and effective code. In this article, we'll take a tour of the fundamental data structures and implementations built into Python and its standard library.]]></description><link>https://www.hackerculture.com/common-python-data-structures/</link><guid isPermaLink="false">63c78bd4df5713004d80db4c</guid><category><![CDATA[Data Structures]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Thu, 02 Mar 2023 15:00:38 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/martin-adams-JWGsMvqzS_0-unsplash--1-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/martin-adams-JWGsMvqzS_0-unsplash--1-.jpg" alt="Choosing the Right Data Structures in Python"><p>In this article, we&apos;ll take a tour of the fundamental data structures and implementations built into Python and its standard library.</p><p>As a Python developer, understanding and utilizing various data structures is a fundamental part of writing efficient and effective code. Each data structure provides a particular way of organizing data so it can be accessed efficiently, and learning when to use each one can greatly improve the performance and readability of your code. This information will also help you shine in Python coding interviews.</p><h3 id="table-of-contents">Table of Contents</h3><ul><li><a href="#dictionaries">Dictionaries</a></li><li><a href="#lists">Lists</a></li><li><a href="#tuple">Tuples</a></li><li><a href="#sets-and-multisets">Sets and Multisets</a></li><li><a href="#stacks">Stacks</a></li><li><a href="#queues">Queues</a></li><li><a href="#priority-queues">Priority Queues</a></li></ul><h2 id="dictionaries">Dictionaries</h2><p>In Python, a dictionary (often referred to as a <code>dict</code>) is a data structure that stores a collection of items, where each item is a key-value pair. It is an unordered collection, which means that the items are not stored in a particular order, and the elements are accessed using keys rather than indexes.</p><h3 id="dictgeneral-purpose-dictionaries">dict - General-purpose Dictionaries</h3><p>Dictionaries are defined using curly braces <code>{}</code>, with keys and values separated by colons <code>:</code> &#x2013; here&apos;s an example of how to work with dictionary:</p><pre><code class="language-Python"># Creating a dictionary
my_dict = {&apos;name&apos;: &apos;John Doe&apos;, &apos;age&apos;: 30, &apos;gender&apos;: &apos;male&apos;}
# Or with the dict constructor
my_dict = dict(name=&apos;John Doe&apos;, age=30, gender=&apos;male&apos;)

# Accessing elements of dictionary
print(my_dict[&apos;name&apos;]) # &apos;John Doe&apos;

# Return default if key is not found
print(my_dict.get(&apos;age&apos;)) # 30
print(my_dict.get(&apos;job&apos;, &apos;Not Found&apos;)) # &apos;Not Found&apos;

# Get the dictionary members
print(my_dict.keys()) # [&apos;name&apos;, &apos;age&apos;, &apos;gender&apos;, &apos;job&apos;]
print(my_dict.values()) # [&apos;Jane Doe&apos;, 30, &apos;male&apos;, &apos;software engineer&apos;]
print(my_dict.items()) # [(&apos;name&apos;, &apos;Jane Doe&apos;), (&apos;age&apos;, 30), (&apos;gender&apos;, &apos;male&apos;),</code></pre><p><strong>Note:</strong> while standard <code>dict</code> instances preserve the insertion order of keys in CPython 3.6 and above; this is just a side effect of the CPython implementation and is not defined in the language spec.</p><h3 id="ordereddictpreserve-insertion-order-of-keys">OrderedDict - Preserve Insertion Order of Keys</h3><p>An <code>OrderedDict</code> is a subclass of the built-in <code>dict</code> class in the <code>collections</code> module of python, that remembers the order in which items were added. It is similar to a regular dictionary, but it maintains the order of the items, so that when you iterate over the items, they are returned in the order they were added. Here&apos;s an example of how to use an <code>OrderedDict</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">from collections import OrderedDict
d = OrderedDict()
d[&apos;task1&apos;] = &apos;complete&apos;
d[&apos;task2&apos;] = &apos;pending&apos;
d[&apos;task3&apos;] = &apos;in-progress&apos;
# Returns the items in order of insertion
print(list(d.items()))</code></pre><figcaption>Use OrderedDict to preserve insertion order</figcaption></figure><p><code>OrderedDict</code> is useful when you want to maintain the order of elements in the dictionary and you need to access the elements in the order they were added. This can be useful in situations where the order of elements is important for the functionality of your code. For example, if you want to keep track of the order in which items were added to a shopping cart, you would use an <code>OrderedDict</code>.</p><h3 id="defaultdict-%E2%80%93-return-default-values-for-missing-keys">defaultdict &#x2013; Return Default Values for Missing Keys</h3><p>A <code>defaultdict</code> works just like a regular dictionary, but it has a default value for items that do not exist yet. This can make your code simpler because you do not have to check whether a key is in the dictionary before adding or retrieving a value. They are especially useful when working with dictionaries where the keys are unknown beforehand or when you want to do some operation on keys that doesn&apos;t exist yet.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from collections import defaultdict

dd = defaultdict(list)
dd[&apos;fruits&apos;].append(&apos;apple&apos;)
dd[&apos;fruits&apos;].append(&apos;banana&apos;)
dd[&apos;vegetables&apos;].append(&apos;potato&apos;)

print(dd)
# {&apos;fruits&apos;: [&apos;apple&apos;, &apos;banana&apos;], &apos;vegetables&apos;: [&apos;potato&apos;]}</code></pre><figcaption>defaultdict provides initial values</figcaption></figure><h3 id="chainmap-%E2%80%93-join-multiple-dictionaries">ChainMap &#x2013; Join Multiple Dictionaries</h3><p>In Python, the <code>collections</code> module provides the <code>ChainMap</code> class, which is a type of data structure that allows you to group multiple dictionaries together into a single, logical view. This can be useful in certain situations where you want to access multiple dictionaries as if they were one dictionary while still keeping them separate.</p><p>One common use case for <code>ChainMap</code> is to handle situations where you want to provide defaults for a configuration or settings but also allow users to override them with their own settings.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from collections import ChainMap
dict1 = {&apos;one&apos;: 1, &apos;two&apos;: 2}  
dict2 = {&apos;three&apos;: 3, &apos;four&apos;: 4}
chain = ChainMap(dict1, dict2)
# ChainMap({&apos;one&apos;: 1, &apos;two&apos;: 2}, {&apos;three&apos;: 3, &apos;four&apos;: 4})
</code></pre><figcaption>Use ChainMap to merge multiple dicts</figcaption></figure><hr><h2 id="lists">Lists</h2><p>A <code>list</code> is an ordered, <strong>mutable</strong> collection of items that can be of any type. Lists are defined by enclosing the elements in square brackets <code>[]</code>, separated by commas. Here&apos;s an example of how to create and use a list in Python:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># Creating a list
my_list = [1, &quot;hello&quot;, 3.14, True]

# Accessing elements of list
print(my_list[0]) # 1
print(my_list[1]) # &apos;hello&apos;
print(my_list[-1]) # True

# iterating over the list
for item in my_list:
    print(item)

# Modifying the list
my_list[0] = &quot;world&quot;
# [&quot;world&quot;, &apos;hello&apos;, 3.14, True]

my_list.append(4)
# [&quot;world&quot;, &apos;hello&apos;, 3.14, True, 4]

my_list.remove(&quot;world&quot;)
# [hello&apos;, 3.14, True, 4]</code></pre><figcaption>Lists have many methods and functions</figcaption></figure><p>Lists are mutable, which means that the elements of a list can be modified after it is created. This is the main difference between lists and tuples.</p><h2 id="tuple">Tuple</h2><p>In Python, a tuple is an <strong>immutable</strong>, ordered collection of items that can be of any type. Tuples are defined by enclosing the elements in parentheses <code>()</code>, separated by commas. For example:</p><figure class="kg-card kg-code-card"><pre><code class="language-python"># Creating a tuple
t = (1, &quot;hello&quot;, 3.14, True)

# Accessing elements of tuple
print(t[0])  # 1
print(t[1])  # &apos;hello&apos;
print(t[-1]) # True

# iterating over tuple
for item in t:
    print(item)</code></pre><figcaption>Use a tuple as a simpler version of list</figcaption></figure><p>Tuples are useful when you want to group a set of related items together, like coordinates or a name and an age, or when you want to ensure that the data remains constant throughout the program execution. You can also use them as keys in dictionaries and as elements of sets because they are immutable.</p><h3 id="namedtuple">NamedTuple</h3><p>The <code>NamedTuple</code> is a subclass of a tuple that allows accessing the elements of the tuple using human-readable names rather than index numbers:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">class Car(NamedTuple):
    make: str
    color: str
    mileage: float

car1 = Car(&apos;Jeep&apos;, &apos;blue&apos;, 9047.3)
# Car(make=&apos;Jeep&apos;, color=&apos;blue&apos;, mileage=9047.3)

# Accessing fields:
car1.mileage # 9047.3

# Fields are immutable:
car1.mileage = 12 # AttributeError: &quot;can&apos;t set attribute&quot;
</code></pre><figcaption>A NamedTuple can be accessed by field name</figcaption></figure><p><code>NamedTuple</code>s are useful when working with tuples that have a clear meaning and intent, as they make it easier to understand the code and reduce the likelihood of bugs caused by using the wrong index numbers.</p><p><code>NamedTuple</code>s make the code more readable and understandable, and they reduce the need for comments or variable names to explain the purpose of the tuple.</p><hr><h2 id="sets-and-multisets">Sets and Multisets</h2><p>A set is an unordered collection of objects that does not allow duplicate elements. Typically, sets are used to quickly test a value for membership in the set, to insert or delete new values from a set, and to compute the union or intersection of two sets.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">vowels = {&apos;a&apos;, &apos;e&apos;, &apos;i&apos;, &apos;o&apos;, &apos;u&apos;}
print(&apos;i&apos; in vowels) # True
letters = set(&apos;alice&apos;)
letters.intersection(vowels) # {&apos;a&apos;, &apos;e&apos;, &apos;i&apos;}</code></pre><figcaption>Intersect two sets in Python</figcaption></figure><h3 id="frozenset-%E2%80%93-immutable-sets">frozenset &#x2013; Immutable Sets</h3><p>The <code>frozenset</code> class implements an immutable version of a <code>set</code> that cannot be changed after it has been constructed (no inserts or deletions.) Because frozen sets are static and hashable, they can be used as dictionary keys or as elements of another set, something that isn&#x2019;t possible with regular (mutable) set objects.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">vowels = frozenset({&apos;a&apos;, &apos;e&apos;, &apos;i&apos;, &apos;o&apos;, &apos;u&apos;})
vowels.add(&apos;p&apos;)  
# AttributeError:  
# &quot;&apos;frozenset&apos; object has no attribute &apos;add&apos;&quot;
</code></pre><figcaption>frozenset is a read only set</figcaption></figure><h3 id="counter-%E2%80%93-multisets">Counter &#x2013; Multisets</h3><p>The <code>collections.Counter</code> class in the Python standard library implements a multiset (<em>or bag</em>) type that allows elements in the set to have more than one occurrence.</p><p>This is useful if we need to keep track of how many times an element is included in the set:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from collections import Counter

inventory = Counter()

loot = {&apos;gold&apos;: 1, &apos;bread&apos;: 3}
inventory.update(loot) 
# Counter({&apos;bread&apos;: 3, &apos;gold&apos;: 1})

len(inventory) # 2
# Note: only unique elements, not total
</code></pre><figcaption>Counter in Python</figcaption></figure><hr><h2 id="stacks">Stacks</h2><p>A useful real-world analogy for a stack data structure is a stack of plates &#x2013; similar to a stack of plates, adding or removing is only possible at the top. To access plates lower in the stack, each plate must be removed one by one (Last In, First Out). Whenever a new plate is added, it is placed on top of the stack.</p><p>Both stacks and queues are linear collections of items. However, the way in which the items are accessed differs. In a queue, the item that was added first is the first to be removed (First In, First Out, or FIFO). Whereas in a stack, the item which was most recently added is the one that is removed first (Last In, First Out, or LIFO).</p><h3 id="stack-using-a-list">Stack using a list</h3><p>The built-in list type can be used as a stack, but be careful to only append and remove items with <code>append()</code> and <code>pop()</code> in order to avoid slow performance from having to move all the items in the list.</p><figure class="kg-card kg-code-card"><pre><code class="language-python"># Create an empty stack
stack = []

# Push elements onto the stack
stack.append(1)
stack.append(2)
stack.append(3)

# Print the stack
print(stack) # [1, 2, 3]

# Pop an element from the stack
x = stack.pop()
print(x) # 3

# Print the remaining stack
print(stack) # [1, 2]
</code></pre><figcaption>Using a list as a stack</figcaption></figure><h3 id="deque-%E2%80%93-general-stacks">deque &#x2013; General Stacks</h3><p>Even though we may be able to use a list as a stack, it may not be obvious to someone else reading our code. It&apos;s best to be explicit about the intent of the code. </p><p>We should use the <code>collections.deque</code> class instead for a safe and fast general-purpose stack implementation. Deques are actually double-ended queues, which can maintain a stack from the left or right side:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from collections import deque

# Create a new deque
d = deque()

# Add elements to the deque
d.append(1)
d.appendleft(2)
d.extend([3, 4, 5])

# Remove elements from the deque
x = d.pop()
y = d.popleft()

print(x) # 5
print(y) # 2
print(d) # deque([1, 3, 4])

# Remove all element 
d.clear()
print(d) # deque([])</code></pre><figcaption>Using deque as a stack</figcaption></figure><h3 id="lifoqueue-%E2%80%93-locking-semantics-for-parallel-computing">LifoQueue &#x2013; Locking Semantics for Parallel Computing</h3><p>The <code>LifoQueue</code> (Last In, First Out) implementation in the Python standard library is synchronized and provides locking behavior to support multiple concurrent producers and consumers.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from multiprocessing import Process, LifoQueue

def worker(q):
    # Get an item from the queue
    item = q.get()
    # Do something with the item
    print(f&apos;Worker: {item}&apos;)
    # Signal that the task is done
    q.task_done()

# Create a LIFO queue
q = LifoQueue()

# Create a worker process
p = Process(target=worker, args=(q,))

# Start the process
p.start()

# Put some items on the queue
for i in range(5):
    q.put(i)

# Wait for all the tasks to be completed
q.join()

# Exit the process
p.join()
</code></pre><figcaption>LifoQueue from multiprocessing is thread safe</figcaption></figure><p>In this example, a <code>LifoQueue</code> is created, and a single worker process is started that runs the <code>worker</code> function. The <code>worker</code> function is passed the <code>LifoQueue</code> as an argument, so the process can access it. The main process then puts five items on the queue and waits for them to be processed by the worker process using <code>q.join()</code>. The <code>worker</code> function takes an item from the queue, does something with it (e.g. print it out), signals that the task is done, and exits. The main process will wait on <code>p.join()</code> until the worker process finishes its task. The output will be as follows:</p><pre><code>Worker: 4
Worker: 3
Worker: 2
Worker: 1
Worker: 0</code></pre><hr><h2 id="queues">Queues</h2><p>Queues are similar to stacks, and the difference between them lies in how items are removed:</p><p>With a <strong>stack</strong>, you remove the item <em>most</em> recently added (<em>Last In, First Out,</em> or <em>LIFO</em>). With a <strong>queue</strong>, you remove the item <em>least</em> recently added (<em>First In, First Out,</em> or <em>FIFO</em>)<strong><em>.</em></strong></p><h3 id="collectionsdeque-%E2%80%93-double-ended-queues">collections.deque &#x2013; Double Ended Queues</h3><p>The deque class implements a double-ended queue that supports adding and removing elements from either end in O(1) time. Because deques support adding and removing elements from either end equally well, they can serve both as queues and as stacks.</p><h3 id="queuequeue-%E2%80%93-locking-semantics-for-parallel-computing">queue.Queue &#x2013; Locking Semantics for Parallel Computing</h3><p>This queue implementation in the Python standard library is synchronized and provides locking semantics to support multiple concurrent producers and consumers.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import threading
import queue

def worker(q):
    while True:
    	# Get an item from the queue
        item = q.get()
        if item is None:
            break
        # Process the item
        print(item)
        # Signal that task is done
        q.task_done()

q = queue.Queue()

# Put some items to the queue
for item in range(5):
    q.put(item)

# Create worker threads
t1 = threading.Thread(target=worker, args=(q,))
t2 = threading.Thread(target=worker, args=(q,))

# Wait for all the tasks to be completed
q.join()

t1.join()
t2.join()</code></pre><figcaption>The Queue class is a thread-safe queue</figcaption></figure><h2 id="priority-queues">Priority Queues</h2><p>Ideally, high-priority tasks on the system (e.g., playing a real-time game) should take precedence over lower-priority tasks (e.g., downloading updates in the background). By organizing pending tasks in a priority queue that uses the task urgency as the key, the task scheduler can quickly select the highest-priority tasks and allow them to run first.</p><h3 id="heapq">heapq</h3><p>Python provides a built-in module that implements a binary heap based on a list, which maintains the minimum element from the list in the first spot. Removing repeatedly from a min heap produces results in sorted order:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">import heapq

items = []
heapq.heappush(items, (2, &apos;code&apos;))
heapq.heappush(items, (1, &apos;eat&apos;))
heapq.heappush(items, (3, &apos;sleep&apos;))

while q:  
    next_item = heapq.heappop(items)
    print(next_item)

# Result:
# &#xA0; (1, &apos;eat&apos;)
# &#xA0; (2, &apos;code&apos;)
#   (3, &apos;sleep&apos;)
</code></pre><figcaption>Maintaining a heap from list</figcaption></figure><h3 id="priorityqueue">PriorityQueue</h3><p>The <code>PriorityQueue</code> class is a thread-safe implementation of a heap that uses locks behind the scenes. This is useful when multiple threads need access to a priority queue simultaneously.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from queue import PriorityQueue

q = PriorityQueue()
q.put((2, &apos;code&apos;))
q.put((1, &apos;eat&apos;))
q.put((3, &apos;sleep&apos;))

while not q.empty():
    next_item = q.get()
    print(next_item)

# Result:
#   (1, &apos;eat&apos;)
#   (2, &apos;code&apos;)
#   (3, &apos;sleep&apos;)
</code></pre><figcaption>PriorityQueue class is a thread-safe heap</figcaption></figure><h2 id="references">References</h2><p><a href="https://docs.python.org/3/tutorial/datastructures.html?ref=hackerculture.com#data-structures">Python Data Structures Docs</a></p><p><a href="https://leetcode.com/explore/interview/card/leetcodes-interview-crash-course-data-structures-and-algorithms/?vacRef=discussbanner&amp;ref=hackerculture.com">Leetcode Data Structures Crash Course</a></p><p><a href="https://realpython.com/python-data-structures/?ref=hackerculture.com">Common Python Data Structures Guide</a></p><p><a href="https://amzn.to/3iAUia3?ref=hackerculture.com">Grokking Algorithms - An Illustrated Guide</a></p><p>***</p><p><strong><strong><strong><strong><strong><strong>Did you find this helpful<strong><strong>?</strong></strong></strong></strong></strong></strong></strong></strong><br>I&#x2019;d love to hear about it. Please let me know in the comments.</p><p><strong><strong><strong><strong><strong><strong><strong><strong>Do you have any questions?</strong></strong></strong></strong></strong></strong></strong></strong><br>Leave your question in a comment below, and we&apos;ll answer it with our best advice.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Fun with Functions in Python]]></title><description><![CDATA[Learning how to work with functions will make understanding advanced features in Python like lambdas and decorators much easier. It also puts you on a path toward functional programming techniques.]]></description><link>https://www.hackerculture.com/functions-in-python/</link><guid isPermaLink="false">63c6dbbadf5713004d80da0c</guid><category><![CDATA[Python]]></category><category><![CDATA[Beginner]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Sun, 19 Feb 2023 15:00:59 GMT</pubDate><content:encoded/></item><item><title><![CDATA[The Zen of Python, by Tim Peters]]></title><description><![CDATA[This little-known feature in Python could change the entire way you think about programming in a few sentences and help you come up with better programs.]]></description><link>https://www.hackerculture.com/zen-of-python/</link><guid isPermaLink="false">63c77633df5713004d80dacf</guid><category><![CDATA[Fundamentals]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Sun, 29 Jan 2023 17:00:54 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/karen-m9oQ1C_--EE-unsplash--1-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/karen-m9oQ1C_--EE-unsplash--1-.jpg" alt="The Zen of Python, by Tim Peters"><p>This little-known feature in Python could change the entire way you think about programming in a few sentences and help you come up with better programs.</p><p>We can thank Tim Peters for putting this easter egg into the language. I&#x2019;ve benefited from revisiting it over the years, and I think Tim&#x2019;s words have made me a better programmer.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">&gt;&gt;&gt; import this</code></pre><figcaption>Enter this in a Python terminal</figcaption></figure><h2 id="the-zen-of-python-by-tim-peters">The Zen of Python, by Tim Peters</h2><p>Beautiful is better than ugly.<br>Explicit is better than implicit.<br>Simple is better than complex.<br>Complex is better than complicated.<br>Flat is better than nested.<br>Sparse is better than dense.<br>Readability counts.<br>Special cases aren&#x2019;t special enough to break the rules.<br>Although practicality beats purity.<br>Errors should never pass silently.<br>Unless explicitly silenced.<br>In the face of ambiguity, refuse the temptation to guess.<br>There should be one&#x2014;and preferably only one&#x2014;obvious way to do it.<br>Although that way may not be obvious at first unless you&#x2019;re Dutch.<br>Now is better than never.<br>Although never is often better than <em>right</em> now.<br>If the implementation is hard to explain, it&#x2019;s a bad idea.<br>If the implementation is easy to explain, it may be a good idea.<br>Namespaces are one honking great idea&#x2014;let&#x2019;s do more of those!</p><p>***</p><p>This applies beyond Python to most other programming languages, frameworks, technologies, and even systems. Hopefully, you will be able to apply them and admire their wisdom.</p>]]></content:encoded></item><item><title><![CDATA[The Ultimate Python Programmer Roadmap (2023)]]></title><description><![CDATA[Whether your goal is to learn python for web development, data science, or machine learning, we'll go over the most important libraries and resources to learn after getting the basics down..]]></description><link>https://www.hackerculture.com/ultimate-python-programmer-roadmap/</link><guid isPermaLink="false">63bd966c76f928004d27d617</guid><category><![CDATA[Beginner]]></category><dc:creator><![CDATA[Reddy Desai]]></dc:creator><pubDate>Tue, 10 Jan 2023 21:42:31 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/A-follow-up-to-the-unfortunate-thread-from-the-weekend-As--1-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/A-follow-up-to-the-unfortunate-thread-from-the-weekend-As--1-.jpg" alt="The Ultimate Python Programmer Roadmap (2023)"><p>Python is the most loved programming language by both beginners and experts, according to multiple surveys. And not only software developers but other people from different disciplines use Python for a variety of different tasks, such as data analysis and visualization, artificial intelligence and machine learning, automation, etc.</p><p>There has never been a better time to learn Python, and with our resources created by professional Python developers with decades of experience, you can learn the skills and techniques that really matter in the real world. You can use Python to build web apps, mobile apps, and desktop applications, as well as software testing and even some actual hacking.</p><p>Whether your goal is to learn python for Web Development, Data Science, or Machine Learning, we&apos;ll go over the most important libraries and resources you need to learn after getting the basics down.</p><h3 id="table-of-contents">Table of Contents</h3><ul><li><a href="#python-basics">Python Basics</a></li><li><a href="#intermediate-python">Intermediate Python</a></li><li><a href="#advanced-python-topics">Advanced Python Topics</a></li><li><a href="#python-for-web-development">Python for Web Development</a></li><li><a href="#python-for-automation">Python for Automation</a></li><li><a href="#python-for-devops">Python for DevOps</a></li><li><a href="#python-for-data-science">Python for Data Science</a></li><li><a href="#python-for-machine-learning">Python for Machine Learning</a></li><li><a href="#tips-for-learning-programming-with-python">Tips for Learning Programming with Python</a></li></ul><h2 id="python-basics">Python Basics</h2><p>First, we need to install python so we can play around with it:</p><p><a href="https://www.hackerculture.com/python-setup/">Setting Up Python and Virtualenvs with Pyenv</a></p><p>Then, you can follow this roadmap to go from Python beginner to knowing <em>&quot;just enough to be dangerous&quot;</em> with Python. While this list doesn&apos;t cover every single feature, it does cover many of Python&#x2019;s most noteworthy features and will enable you to read and write Python programs.</p><ol><li><a href="https://www.hackerculture.com/intro-to-python/">A Gentle Introduction to Python Programming</a></li><li><a href="https://www.hackerculture.com/intro-to-python/">Variables and Operators</a></li><li>Strings and String Methods</li><li><a href="https://www.hackerculture.com/basic-python-data-types/">Basic Data Types in Python</a></li><li>Pythonic Loops</li><li>Exception handling</li><li><a href="https://realpython.com/courses/defining-and-calling-functions/?ref=hackerculture.com">Functions</a></li><li>Importing libraries</li></ol><h2 id="intermediate-python">Intermediate Python</h2><p>This section will give you the tools to make complex applications and introduce you to concepts about Object Oriented Programming (OOP) in Python:</p><ol><li><a href="https://realpython.com/courses/formatting-python-strings/?ref=hackerculture.com">String Formatting</a></li><li><a href="https://realpython.com/courses/using-list-comprehensions-effectively/?ref=hackerculture.com">List Operations</a></li><li><a href="https://realpython.com/courses/python-basics-dictionaries/?ref=hackerculture.com">Dictionaries</a></li><li><a href="https://realpython.com/courses/intro-object-oriented-programming-oop-python/?ref=hackerculture.com">Classes and Objects</a></li><li><a href="https://realpython.com/courses/python-basics-file-system-operations/?ref=hackerculture.com">File System Operations</a></li><li><a href="https://www.hackerculture.com/common-data-structures-python/">Common Data Structures in Python</a></li></ol><h2 id="advanced-python-topics">Advanced Python Topics</h2><p>This section builds upon the previous ones and teaches you how to use frameworks and libraries available in the Python language:</p><ol><li><a href="https://www.hackerculture.com/python-asyncio-guide-to-asynchronous-programming/">Asynchronous Programming with asyncio</a></li><li><a href="https://www.hackerculture.com/python-threads-multithreading-guide/">Multithreading</a></li><li><a href="https://www.hackerculture.com/running-processes-python-multiprocessing-guide/">Multiprocessing</a></li></ol><p>Once you have gone through all the previous steps, you should have everything you need to make use of Python in real-world applications. </p><p>Python is used in almost every industry, and there are many possible career paths you can choose from. Let&apos;s look at some of them, shall we?</p><hr><h2 id="python-for-web-development">Python for Web Development</h2><p>The role of a web developer is to build websites and applications that are accessible using a web browser. Python is typically used on the backend (server) side.</p><p>Start by learning more about the web and how your users will interact with your apps on their browsers. This course will help you become more of a well-rounded full-stack web developer:</p><p><a href="https://www.coursera.org/specializations/web-design?ref=hackerculture.com">Web Design Course for Everybody</a></p><p>A popular web framework that many companies use to build web applications is <strong>Flask</strong>. This tutorial will help you become familiar with building apps with Flask:</p><p><a href="https://realpython.com/flask-connexion-rest-api/?ref=hackerculture.com">Python REST APIs with Flask</a></p><p>You should also become familiar with databases. This tutorial covers how to connect a database to your Flask server:</p><p><a href="https://realpython.com/flask-connexion-rest-api-part-2/?ref=hackerculture.com">Connecting a Database to your Flask Project with SQLAlchemy</a></p><p>You can also learn how to deploy Flask apps so you can share them with the world in this article:</p><p><a href="https://realpython.com/courses/deploy-python-script-web-flask/?ref=hackerculture.com">How to deploy your Flask apps</a></p><hr><h2 id="python-for-automation">Python for Automation</h2><p>Automation is the field of performing tasks that are repetitive or time-consuming, such as data entry, web scraping, and automation of various system processes, in a way that they can be reused and repeated effortlessly.</p><p>We do a lot of automation for running integration tests in software projects, in which we write scripts to interact with an existing program and report errors.</p><p><a href="https://realpython.com/python-testing/?ref=hackerculture.com">Getting started with testing in Python</a></p><p>Automation can also be used to control other software, such as a web browser, Excel, or even hardware, such as robotic systems. Some libraries worth checking out for this are:</p><ul><li><strong>Selenium</strong></li><li><strong>Beautiful Soup</strong></li></ul><hr><h2 id="python-for-devops">Python for DevOps</h2><p>DevOps engineers are responsible for implementing and maintaining infrastructure to support software systems, and they work to ensure that software systems are deployed quickly, efficiently, and with minimal downtime.</p><p>This book provides a comprehensive introduction to the field of DevOps:</p><p><a href="https://amzn.to/3GToK8H?ref=hackerculture.com">Python for DevOps: Learn Ruthlessly Effective Automation</a></p><p>Some of the other technologies used by DevOps engineers to automate software deployments and maintain a smooth operation are:</p><ul><li><strong>Docker</strong></li><li><strong>Kubernetes</strong></li></ul><hr><h2 id="python-for-data-science">Python for Data Science</h2><p>A data scientist&apos;s primary concern is to extract insights and knowledge from data. This typically involves a number of different tasks, including collecting and cleaning data, developing predictions and forecasts using statistics, and visualizing results.</p><p>A data scientist must be familiar with databases and should be able to query large amounts of data in order to identify patterns and trends. Usually, the main tools used by data scientists are:</p><ol><li><a href="https://www.codecademy.com/learn/learn-sql?ref=hackerculture.com">Free Interactive SQL Course</a></li><li><a href="https://pandas.pydata.org/docs/getting_started/intro_tutorials/?ref=hackerculture.com">Pandas</a></li><li><a href="https://www.simplilearn.com/tutorials/python-tutorial/data-visualization-in-python?ref=hackerculture.com">Data Visualization</a></li></ol><p>If you want to get deeper into data science, consider the following books:</p><p><a href="https://amzn.to/3vR97IT?ref=hackerculture.com">Data Science from Scratch</a></p><p><a href="https://amzn.to/3XhZOx6?ref=hackerculture.com">Python for Data Analysis: Data Wrangling with Pandas, NumPy, IPython</a></p><hr><h2 id="python-for-machine-learning">Python for Machine Learning</h2><p>A machine learning engineer creates and develops computer programs that can &quot;learn&quot; from data and make predictions or decisions without being explicitly programmed to do so to solve real-world problems like predicting the weather or detecting facial expressions.</p><p>This typically involves working with large sets of data, which can be explored with the <strong>pandas</strong> library to prepare the data for use in models:</p><p><a href="https://pandas.pydata.org/docs/getting_started/intro_tutorials/?ref=hackerculture.com">Getting started with Pandas in Python</a></p><p>A very popular library that helps with training machine learning models for many common use cases is <a href="https://www.simplilearn.com/tutorials/python-tutorial/scikit-learn?ref=hackerculture.com">Scikit-learn</a>.</p><p>If you are interested in models that use language, check out this article about NLP and Python:</p><p><a href="https://towardsai.net/p/nlp/natural-language-processing-nlp-with-python-tutorial-for-beginners-1f54e610a1a0?ref=hackerculture.com">Natural Language Processing (NLP) with Python</a></p><p>These books are an excellent way to get into machine learning with Python, with many examples and some actual real-world projects:</p><p><a href="https://amzn.to/3QwgPBB?ref=hackerculture.com">Introduction to Machine Learning with Python</a></p><p><a href="https://amzn.to/3k9sMRp?ref=hackerculture.com">Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow</a></p><hr><h2 id="tips-for-learning-programming-with-python">Tips for Learning Programming with Python</h2><p>Learning how to write computer programs is hard, but learning Python, in particular, is significantly easier than many other programming languages. These tips will help you stay focused and motivated to achieve your goals:</p><ul><li>Have patience. Learning a new language always requires more effort and time, so understand that it will take time to let everything sink in.</li><li>Don&apos;t feel intimidated by complex terms, errors, and issues. Don&apos;t give up. This happens to everyone in programming. Research the topic using other resources and come back to it with a clear mind.</li><li>Frustration and pain are a part of the process. Embrace them instead of avoiding them, and you will conquer them.</li><li>Be consistent. If you are not consistent in learning, it will take a lot more time and effort.</li><li>Don&apos;t try to learn everything at once. It&apos;s better to practice a specific topic every day before moving on to the next.</li><li>Build small projects to try out the things you learn and build up your confidence. You should add them to your github profile to help you land jobs.</li></ul><p>***</p><p><strong><strong><strong>Did you find this helpful<strong>?</strong></strong></strong></strong><br>I&#x2019;d love to hear about it. Please let me know in the comments.</p><p><strong><strong><strong><strong>Do you have any questions?</strong></strong></strong></strong><br>Leave your question in a comment below, and we&apos;ll answer it with our best advice.</p>]]></content:encoded></item><item><title><![CDATA[Python asyncio - A Guide to Asynchronous Programming]]></title><description><![CDATA[In this guide, we'll introduce asynchronous programming in Python and review fundamental concepts like how to define, create and run asynchronous functions, coroutines, and some common use cases and best practices.]]></description><link>https://www.hackerculture.com/python-asyncio-guide-to-asynchronous-programming/</link><guid isPermaLink="false">63ac8daf529367003d3a6d48</guid><category><![CDATA[Python]]></category><category><![CDATA[concurrency]]></category><category><![CDATA[asyncio]]></category><category><![CDATA[asynchronous]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Mon, 09 Jan 2023 22:00:13 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/olaf-ZT3mEdrBS7Y-unsplash.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/olaf-ZT3mEdrBS7Y-unsplash.webp" alt="Python asyncio - A Guide to Asynchronous Programming"><p>In this guide, we&apos;ll introduce asynchronous programming in Python and review fundamental concepts like how to define, create and run asynchronous functions, coroutines, and some common use cases and best practices.</p><p>You may have seen asyncio in a new project you started working on. Or perhaps in a code example of how to perform a common task:</p><ul><li>Perhaps you need to use an API and the code examples use asyncio.</li><li>Or you need to integrate a new library that uses asyncio.</li><li>Or you found some code snippets that use asyncio.</li></ul><p>Once you understand the concepts in this guide, you will be able to develop programs that can leverage the asyncio library in Python to process many tasks concurrently and make better use of your machine resources, such as additional CPU cores.</p><h3 id="table-of-contents">Table of Contents</h3><ol><li><a href="#introduction-to-asynchronous-programming">Introduction to Asynchronous Programming</a></li><li><a href="#getting-started-with-asyncio-in-python">Getting Started with Asyncio in Python</a></li><li><a href="#what-is-the-event-loop">Working with Asyncio Event Loops</a></li><li><a href="#what-are-coroutines">What Are Coroutines</a></li><li><a href="#using-asyncio-for-network-programming">Using Asyncio for Network Programming</a></li><li><a href="#advanced-asyncio-concepts">Advanced Asyncio Concepts</a></li><li><a href="#when-to-use-asyncio">When to Use Asyncio</a></li><li><a href="#when-not-to-use-asyncio">When Not to Use Asyncio</a></li><li><a href="#best-practices-for-asynchronous-programming-in-python">Best Practices for Asynchronous Programming in Python</a></li></ol><h2 id="introduction-to-asynchronous-programming">Introduction to Asynchronous Programming</h2><p><strong>Asynchronous</strong> refers to the concept of not occurring at the same time as something else. In the context of programming, asynchronous refers to the execution of tasks that do not block the flow of execution of other tasks. This means that multiple tasks can be performed concurrently, rather than sequentially.</p><p>This is achieved by using asynchronous function calls, which return control to the calling function before the task is completed, allowing other tasks to be executed in the meantime.</p><p>For example, we can make an asynchronous function call. This will issue the request to make the function call and will not wait around for the call to complete. We can choose to check on the status or result of the function call later.</p><p>One of the main benefits of asynchronous programming is improved performance, as it allows for non-blocking IO operations and allows multiple tasks to be performed simultaneously.</p><h2 id="getting-started-with-asyncio-in-python">Getting Started with Asyncio in Python</h2><p>The <code>asyncio</code> module contains utilities to implement asynchronous programming in Python.</p><p>It allows you to write concurrent code using asynchronous programming techniques rather than traditional thread-based concurrent programming.</p><p>You may be wondering how asyncio differs from traditional thread-based concurrent programming in Python...</p><p>Asyncio is particularly useful in Python because it allows you to write concurrent code in a single-threaded environment, which can be more efficient and easier to work with than using multiple threads.</p><h3 id="understanding-the-async-await-syntax">Understanding the async, await syntax</h3><p>We use <code>def async</code> to define asynchronous functions in Python. These are special types of functions that don&apos;t execute right away when called. Instead, they are scheduled, and we must then use <code>await</code> to execute and wait for the function to return.</p><p>This last point is key and is the reason why async functions are so powerful.</p><p>We can also execute async functions in other parts of our program with <code>asyncio.run()</code></p><figure class="kg-card kg-code-card"><pre><code class="language-Python">async def an_async_function():
    await asyncio.sleep(1)
    print(&quot;Hello, World!&quot;)

asyncio.run(my_async_function())
</code></pre><figcaption>Running async functions in Python with asyncio.run</figcaption></figure><h2 id="what-is-the-event-loop">What is the Event Loop</h2><p>The <strong>event loop</strong> is the central mechanism in asyncio that schedules and runs asynchronous tasks. It works by continuously polling for events and running the appropriate tasks when they are ready.</p><p>You can think of the event loop as a kind of &quot;coordinator&quot; that manages the execution of asynchronous tasks.</p><h3 id="creating-and-customizing-event-loops">Creating and customizing event loops</h3><p>By default, asyncio will create an event loop for you when you use <code>asyncio.run()</code> or call <code>asyncio.AbstractEventLoop.run_until_complete()</code>. However, you can also create and customize your own event loop if needed.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">loop = asyncio.get_event_loop()
loop.run_until_complete(an_async_function())
loop.close()</code></pre><figcaption>How to get the current event loop</figcaption></figure><p>The event loop starts with the <code>loop.run_until_complete()</code> method, which blocks until all tasks have completed. Finally, the event loop is closed with the <code>loop.close()</code> method.</p><p>Here are some other ways you can run an event loop in Python using the <code>asyncio</code> module:</p><ol><li><code>loop.run_forever()</code>: This method runs the event loop indefinitely. It blocks until the event loop is stopped with the <code>loop.stop</code> method or until an exception is raised.</li><li><code>loop.run_until_complete(future)</code>: This method runs the event loop until the given <code>future</code> is completed. It blocks until the <code>future</code> is done or until an exception is raised.</li><li><code>loop.run_in_executor(executor, func, *args)</code>: This method runs the given <code>func</code> in a thread or process executor and returns a <code><strong>Future</strong></code> object. The event loop will run until the <code>Future</code> is done or until an exception is raised.</li><li><code>loop.run_as_soon_as_possible(callback)</code>: This method schedules the given <code>callback</code> to be run as soon as possible. It does not block.</li><li><code>loop.call_later(delay, callback, *args)</code>: This method schedules the given <code>callback</code> to be run after the specified <code>delay</code> in seconds. It does not block.</li></ol><h2 id="what-are-coroutines">What Are Coroutines</h2><p>A coroutine is a task that can be suspended and resumed. In the context of asynchronous programming, when we talk about tasks that can be performed concurrently, we are referring to coroutines.</p><p>Therefore, coroutines are the unit of concurrency used in asyncio programs.</p><p>Many coroutines can be created and executed at the same time. They have control over when they will suspend and resume, allowing them to cooperate as to when concurrent tasks are executed.</p><p>This is called <strong>cooperative multitasking</strong> and is different from the multitasking typically used with threads called <em>preemptive multitasking</em>.</p><p>A coroutine can be defined using the <code>async def</code> expression. It can take arguments and return a value, just like a function. Calling a coroutine function will create a coroutine object, but it <strong>does not</strong> execute the coroutine function right away.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">async def my_coroutine(delay):
    await asyncio.sleep(delay)
    print(f&apos;Finished waiting for {delay} seconds&apos;)

# creates but does not execute the coroutine
coroutine_1 = my_coroutine(1)
# await starts the coroutine and blocks until finished
result = await my_coroutine(2)</code></pre><figcaption>Creating and awaiting coroutines</figcaption></figure><p>When a coroutine is created but never executed, we will see a warning like this when the Python program finishes:</p><pre><code class="language-bash">sys:1: RuntimeWarning: coroutine &apos;my_coroutine&apos; was never awaited</code></pre><p>We can avoid this warning and support top-level <code>await</code> expressions by starting the Python interpreter with a default event loop. This is also the default in IPython.</p><pre><code class="language-bash">python -m asyncio script.py</code></pre><p>Having top level <code>await</code> is neat, but it can cause problems too. There can only be one event loop running in a thread. If any of your code tries to launch an event loop, perhaps by calling <code>asyncio.run()</code>, you&apos;ll get an error, so be careful.</p><h3 id="whats-so-great-about-coroutines">What&apos;s so great about coroutines</h3><p>Threads and processes achieve multitasking managed by the operating system (OS) that chooses which threads and processes should run, when, and for how long. The OS switches between threads and processes rapidly, suspending those that are not running and resuming those granted time to run. This is called <strong>preemptive multitasking</strong>.</p><p>Coroutines in Python provide an alternative type of multitasking called <strong>cooperating multitasking</strong>. This allows coroutines to cooperate by design, choosing how and when to suspend their execution.</p><p>Another key aspect of coroutines is that they are more lightweight than threads. This means they are faster to start and use less memory. They may use less than 1 KB of memory to execute. Essentially a coroutine is a special type of function, whereas a thread is represented by a Python object associated with an operating system thread with which the object must interact.</p><p>Coroutines are a new alternate, interesting, and powerful approach to concurrency, different from thread-based and process-based concurrency. As such, we may have thousands of threads in a Python program, but we could easily have tens or hundreds of thousands of coroutines just in one thread.</p><p>You can also schedule coroutines for execution in separate threads, with <code><a href="https://www.hackerculture.xyz/python-threads-multithreading-guide/?ref=hackerculture.com#threadpoolexecutor">ThreadPoolExecutor</a></code>, or separate processes using <code><a href="https://www.hackerculture.xyz/running-processes-python-multiprocessing-guide/?ref=hackerculture.com#processpoolexecutor">ProcessPoolExecutor</a></code>.</p><h3 id="how-to-run-blocking-tasks-with-asyncio">How to Run Blocking Tasks with Asyncio</h3><p>We often need to execute a blocking function call within an asyncio application because, in practice, most workloads include a mix of IO-bound operations and also CPU-bound operations.</p><p>This could be for many reasons, such as:</p><ul><li>To execute a CPU-bound task like calculating something.</li><li>To execute a blocking IO-bound task like reading or writing from a file.</li><li>To call into a third-party library that does not support asyncio yet.</li></ul><p>Making a blocking call directly in an asyncio program will cause the event loop to stop while the blocking call is executing. It will not allow other coroutines to run in the background.</p><p>This can be prevented by running the blocking call outside of the event loop, which we can do with <code>asyncio.to_thread()</code>.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># execute a function in a separate thread
await asyncio.to_thread(my_blocking_task)</code></pre><figcaption>How to run blocking task with asyncio</figcaption></figure><p>The <strong><strong><code>asyncio.to_thread()</code></strong></strong> function takes a function to execute and any arguments. It returns a coroutine that can be awaited or scheduled as an independent task. The function is then executed in a separate thread.</p><p>The <strong><strong><code>asyncio.to_thread()</code></strong></strong> function creates a <strong><strong><code>ThreadPoolExecutor</code></strong></strong> behind the scenes to execute blocking calls.</p><p>As such, the <code><strong><strong>asyncio.to_thread()</strong></strong></code> function is only appropriate for IO-bound tasks, and we should not use this method of asyncio for CPU bound tasks.</p><h2 id="using-asyncio-for-network-programming">Using Asyncio for Network Programming</h2><p>Network calls are a good use case for asyncio since they&apos;re IO-bound, and we can just fire and forget the write or read<em>,</em> and our program can go on to perform other tasks. The caller does not need to wait for the operation to complete before returning, so we should not waste time just checking on them.</p><p>The read and write operations are performed somehow (e.g., by the underlying operating system or perhaps a task queue like Kafka), and the status of the action and/or data is retrieved by the caller later, once available, or when the caller is ready.</p><p><strong>Non-blocking</strong> IO is a way of performing IO where reads and writes are requested, although performed asynchronously. The caller does not need to wait for the operation to complete before returning. Non-blocking IO is implemented in practice with asynchronous programming.</p><p>Let&apos;s go through a few asyncio examples that implement non-blocking IO in client and server networking:</p><h3 id="http-server">HTTP server</h3><p>This is an example of an asyncio-based HTTP server that serves static files from a given directory. It uses the <code>aiohttp</code> library to handle HTTP requests and responses.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import asyncio
from aiohttp import web

async def handle(request):
    with open(&apos;index.html&apos;, &apos;rb&apos;) as f:
        return web.Response(body=f.read(), content_type=&apos;text/html&apos;)

async def main():
    app = web.Application()
    app.add_routes([web.get(&apos;/&apos;, handle)])
    return app

if __name__ == &apos;__main__&apos;:
    web.run_app(main())
</code></pre><figcaption>Async web server in Python</figcaption></figure><h3 id="websocket-server">WebSocket server</h3><p>This is an example of an asyncio-based WebSocket server that broadcasts messages to all connected clients. It uses the <code>websockets</code> library to handle WebSocket connections.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import asyncio
import websockets

async def broadcast(websockets, message):
    for ws in websockets:
        await ws.send(message)

async def handler(websocket, path):
    async for message in websocket:
        await broadcast(websockets, message)

async def main(host, port):
    server = await websockets.serve(handler, host, port)
    await server.wait_closed()

if __name__ == &apos;__main__&apos;:
    asyncio.run(main(&apos;127.0.0.1&apos;, 8888))
</code></pre><figcaption>Async websockets server and handlers</figcaption></figure><h3 id="websocket-client">WebSocket client</h3><p>This is an example of an asyncio-based WebSocket client that connects to a server, sends a message, and prints the response. It uses the <code>websockets</code> library to handle the WebSocket connection.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import asyncio
import websockets

async def main():
    async with websockets.connect(&apos;ws://localhost:8888&apos;) as websocket:
        await websocket.send(&quot;Hello, world!&quot;)
        response = await websocket.recv()
        print(f&apos;Received: {response}&apos;)

asyncio.run(main())
</code></pre><figcaption>Async websockets client connections</figcaption></figure><h2 id="advanced-asyncio-concepts">Advanced Asyncio Concepts</h2><p>So far, we&apos;ve only seen examples of how to run a single asynchronous function to perform non-blocking IO operations.</p><p>When we have many operations to run concurrently, we need to coordinate and manage those operations. Fortunately, the asyncio module has some functions that make it easier for us.</p><h3 id="how-to-use-asynciogather-to-wait-for-multiple-async-tasks">How to use asyncio.gather() to wait for multiple async tasks</h3><figure class="kg-card kg-code-card"><pre><code class="language-Python">import asyncio

async def task_one():
    print(&quot;Starting task one&quot;)
    await asyncio.sleep(1)
    print(&quot;Finishing task one&quot;)
    return 1

async def task_two():
    print(&quot;Starting task two&quot;)
    await asyncio.sleep(2)
    print(&quot;Finishing task two&quot;)
    return 2

async def main():
    # This will wait for all the coroutines
    results = await asyncio.gather(task_one(), task_two())
    print(results)

asyncio.run(main())
</code></pre><figcaption>Using asyncio.gather to wait for coroutines</figcaption></figure><p>This code will produce the following output:</p><pre><code>Starting task one
Starting task two
Finishing task one
Finishing task two
[1, 2]
</code></pre><p>As you can see, the tasks are run concurrently rather than sequentially. This can be useful for improving the performance of an application by allowing it to make better use of available CPU resources.</p><p>The <code><strong>asyncio.gather()</strong></code> method accepts many arguments and returns a list of the results returned by all the coroutines.</p><p>It is also common to create many coroutines beforehand and then gather them later. We can collect many coroutines together into a list either manually or using a list comprehension. We can then unpack this list as arguments to <code>gather()</code>. Note the use of the star operator (<code>*</code>) here:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># creates many coroutines
coros = [my_async_task(i) for i in range(10)]
...
# then, run the tasks
results = await asyncio.gather(*coros)</code></pre><figcaption>How to await a list of coroutines using asyncio.gather</figcaption></figure><h3 id="how-to-use-asynciowait-to-manage-multiple-asynchronous-tasks">How to use asyncio.wait() to manage multiple asynchronous tasks</h3><p>The <code><strong>asyncio.wait()</strong></code> function blocks and returns a tuple containing two sets of tasks: those that have completed and those that are still pending. In this example, we iterate over the completed tasks and print their results.</p><p>Using the <code>asyncio.wait()</code> function, you can easily manage multiple asynchronous tasks and perform actions when they have completed. This can be particularly useful for situations where you need to wait for multiple tasks to complete before moving on to the next step of your program and for setting timeouts for some tasks.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># Run these tasks and wait 
tasks = [task_one(), task_two()]
    completed, pending = await asyncio.wait(tasks)

    results = [t.result() for t in completed]
    print(results)
</code></pre><figcaption>Using asyncio.wait to manage async tasks</figcaption></figure><p>Alternatively, we could use the <code>asyncio.as_completed()</code> function to run concurrent tasks and iterate their results as they become available. This is possible because <code>asyncio.as_completed()</code> returns an iterable of the coroutines that can be awaited.</p><h3 id="chaining-multiple-asynchronous-tasks">Chaining multiple asynchronous tasks</h3><p>There are situations where you may need to perform a series of asynchronous tasks in a specific order. Since coroutines are awaitable, another coroutine can <code>await</code> it. You may use this class to chain async tasks since the <code>asyncio</code> library does not (yet) provide a similar function.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">class Chain:
    &quot;A class to execute async tasks sequentially&quot;
    def __init__(self, tasks):
        self.tasks = tasks

    async def __call__(self, *args, **kwargs):
        for task in self.tasks:
            args = await task(*args, **kwargs)
            args = (result,)
        return result</code></pre><figcaption>The Chain class executes async tasks sequentially</figcaption></figure><p>To use this class, you can pass a list of asynchronous tasks to the constructor and then call the <code>Chain</code> instance to execute the tasks in the specified order. The result of each task is passed as an argument to the next task in the chain.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">chain = Chain([task_one, task_two])
# block until tasks are performed synchronously
result = await chain() # returns the last task result</code></pre><figcaption>Running tasks in sequence with Chain</figcaption></figure><h2 id="when-to-use-asyncio">When to Use Asyncio</h2><p>Coroutines are an alternative to threading-based concurrency that is provided by the Python language and further supported by the asyncio module. They are suited to non-blocking IO with subprocesses and sockets. However, blocking IO and CPU-bound tasks can be used in a simulated non-blocking manner using threads and processes under the covers.</p><p>Any program written using threads or processes can be rewritten or instead written using coroutines if we so desire.</p><p>A coroutine is even more lightweight than a process. Processes, like threads, are created and managed by the underlying operating system and are represented by a <strong><strong>multiprocessing.Process</strong></strong> object.</p><p>This means that coroutines are significantly faster than a process to create and start and take up much less memory. A coroutine is just a special function, whereas a Process is an instance of the interpreter that has at least one thread.</p><p>As such, we may have thousands of threads in a Python program, but we could easily have tens or hundreds of thousands of coroutines all in one thread.</p><h2 id="when-not-to-use-asyncio">When Not to Use Asyncio</h2><p>There are many misconceptions about Python concurrency and especially around asyncio. Using <code>asyncio</code> does not magically solve all the issues with Python.</p><p>For example:</p><ul><li>Asyncio works around the Global Interpreter Lock (GIL).</li><li>Asyncio is faster than using threads.</li><li>Asyncio avoids the need for locks and other synchronization methods.</li><li>Asyncio is easier to use than threads.</li></ul><p>These are all <strong>False</strong>.</p><p>Only a single coroutine can run at a time by design, and they cooperate to execute. This is just like threads under the GIL. However, the GIL still applies to <code>asyncio</code> code, and the performance of <code>asyncio</code> programs may be affected by the GIL in the same way as multithreaded programs. So <code>asyncio</code> is not faster and is also not multithreaded and not parallel.</p><p>Any program you can write with <code>asyncio</code>, you can also write with threads, and it could actually be as fast or even faster. It could also probably be simpler and easier to read and interpret by other developers.</p><p>If you need to bypass the GIL and take full advantage of multiple CPU cores in your Python program, you may want to consider using a different concurrency framework like <code><a href="https://www.hackerculture.xyz/running-processes-python-multiprocessing-guide/?ref=hackerculture.com">multiprocessing</a></code> and other libraries such as <code>concurrent.futures</code>. These frameworks allow you to create multiple processes, each with its own Python interpreter and GIL, which can run in parallel on separate CPU cores.</p><p>Any concurrency pitfalls you might expect with threads, you can also encounter with coroutines. You must ensure coroutines are thread safe and safe from deadlocks and race conditions, just like with threads or processes.</p><p>Another reason not to use <code>asyncio</code> is that you may not like asynchronous programming. Even though asynchronous programming has been popular for some time now in various programming communities, it is different from procedural, object-oriented, and functional programming, and some developers just don&#x2019;t like it.</p><p>If you don&#x2019;t like it, don&#x2019;t use it. It&#x2019;s a fair reason.</p><p>You can achieve concurrency in many ways other than using asynchronous programming, using threads or processes as needed.</p><p>You can learn all about threads and processes in our guides:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.hackerculture.com/running-processes-python-multiprocessing-guide/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Running multiple processes in Python - a multiprocessing guide</div><div class="kg-bookmark-description">Learn how to write Python programs that can run simultaneously on all processors available and also as background processes.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.hackerculture.com/favicon.ico" alt="Python asyncio - A Guide to Asynchronous Programming"><span class="kg-bookmark-author">Hacker Culture</span><span class="kg-bookmark-publisher">Adrian Cruz</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1672224745017-a9b54ad9188f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8YWxsfDI5fHx8fHx8Mnx8MTY3MjI0ODY3MQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Python asyncio - A Guide to Asynchronous Programming"></div></a></figure><h2 id="best-practices-for-asynchronous-programming-in-python">Best Practices for Asynchronous Programming in Python</h2><p>These are some tips that will have you writing efficient and maintainable asynchronous code and save you lots of headaches:</p><h3 id="use-asyncios-high-level-apis-whenever-possible">Use asyncio&apos;s high-level APIs whenever possible</h3><p>The asyncio library provides several high-level APIs that make it easy to write concurrent code. For example, you can use the <code>gather</code> function to run multiple tasks concurrently or the <code>as_completed</code> function to iterate over a group of tasks as they complete. These APIs can help you write efficient and maintainable code, as they abstract away many of the low-level details of concurrent programming.</p><h3 id="avoid-blocking-the-event-loop">Avoid blocking the event loop</h3><p>The event loop is the heart of asyncio, and it is responsible for scheduling and executing asynchronous tasks. If you block the event loop, you can cause performance issues and disrupt the execution of other tasks. To avoid blocking the event loop, use non-blocking IO operations whenever possible, and avoid using blocking functions such as <code>time.sleep</code> or <code>threading.Lock</code>.</p><h3 id="use-async-context-managers-and-async-iterators">Use async context managers and async iterators</h3><p>Async context managers and async iterators can help you write asynchronous code that is easy to understand and maintain. For example, you can use async context managers to manage resources that need to be acquired and released asynchronously, or you can use async iterators to iterate over asynchronous data streams in a natural and intuitive way.</p><h3 id="common-pitfalls-to-avoid-when-doing-asynchronous-programming">Common pitfalls to avoid when doing asynchronous programming</h3><ol><li>Forgetting to <code>await</code> asynchronous tasks: When you <code>await</code> an asynchronous task, you are telling the event loop to execute that task and pause the current task until the awaited task completes. If you forget to <code>await</code> a task, it will be scheduled to run, but the event loop will not wait for it to complete before moving on to the next task. This can lead to unexpected behavior and race conditions.</li><li>Not using asynchronous functions when appropriate: On the other hand, it is important to use asynchronous functions when appropriate, as this can significantly improve the performance and scalability of your application. If you have long-running tasks or tasks that perform IO operations, consider using asynchronous functions to ensure that they do not block the event loop.</li><li>Not using synchronization techniques: Any concurrency pitfalls you might expect with threads, you can also encounter with coroutines. While only one coroutine can run within the event loop at one time, they can be suspended and resumed while using a shared variable. You must ensure coroutines are safe from deadlocks and race conditions, just like with threads or processes.</li></ol><p>If you keep these practices in mind and avoid the multiple pitfalls related to concurrency and asynchronous programming, you should be able to develop Python programs that can process many tasks concurrently.</p><p>You may even want to experiment with running asynchronous tasks combined with other concurrency techniques, especially when pairing asyncio with multiprocessing and &#xA0;<a href="https://docs.python.org/3/library/asyncio-eventloop.html?ref=hackerculture.com#asyncio.loop.run_in_executor">concurrent.futures.Executor</a> instances.</p><h2 id="references">References</h2><ul><li><a href="https://docs.python.org/3/library/asyncio-eventloop.html?ref=hackerculture.com">Event Loop</a></li><li><a href="https://docs.python.org/3/library/asyncio-task.html?ref=hackerculture.com">Coroutines</a></li><li><a href="https://amzn.to/3Q47hh3?ref=hackerculture.com">Using Asyncio in Python</a></li><li><a href="https://superfastpython.com/python-asyncio/?ref=hackerculture.com">Python Asyncio The Complete Guide</a></li></ul><p><strong><strong>How are you using </strong>asyncio<strong> in your programs?</strong></strong><br>I&#x2019;d love to hear about it. Please let me know in the comments.</p><p><strong><strong>Do you have any questions?</strong></strong><br>Leave your question in a comment below, and I&apos;ll answer it with my best advice.</p>]]></content:encoded></item><item><title><![CDATA[Threads in Python - The Multithreading Guide]]></title><description><![CDATA[In this guide, you will learn about threads, their use cases, and how to use them to run multiple parts of your Python programs concurrently. When used correctly, they can make your code run faster and more efficiently with very little effort.]]></description><link>https://www.hackerculture.com/python-threads-multithreading-guide/</link><guid isPermaLink="false">63ab2ba7529367003d3a68b3</guid><category><![CDATA[Python]]></category><category><![CDATA[concurrency]]></category><category><![CDATA[threads]]></category><category><![CDATA[multithreading]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Mon, 02 Jan 2023 13:00:45 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/photo-1619472351888-f844a0b33f5b_crop-entropy-cs-tinysrgb-fit-max-fm-jpg-ixid-MnwxMTc3M3wwfDF8c2VhcmNofDExfHx0aHJlYWRzfGVufDB8fHx8MTY3MjE3MjI0Mg-ixlib-rb-4.0--1-.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/photo-1619472351888-f844a0b33f5b_crop-entropy-cs-tinysrgb-fit-max-fm-jpg-ixid-MnwxMTc3M3wwfDF8c2VhcmNofDExfHx0aHJlYWRzfGVufDB8fHx8MTY3MjE3MjI0Mg-ixlib-rb-4.0--1-.webp" alt="Threads in Python - The Multithreading Guide"><p>Threads have been available since Python 2, but they are often misunderstood and misused because of the limitations and capabilities of threads in Python. When used correctly, they can make your code run faster and more efficiently with very little effort.</p><p>In this guide, you will learn about threads, their use cases, and how to use them to run multiple parts of your Python programs concurrently. We&apos;ll also go over some alternatives and scenarios where threads may not be a good option.</p><p>By the end of this guide, you will be able to develop programs with improved performance by applying multithreading techniques that make better use of your machine resources, such as additional CPU cores.</p><h3 id="table-of-contents">Table of Contents</h3><ol><li><a href="#what-is-a-thread">What is a Thread</a></li><li><a href="#threads-and-processes">Threads and Processes</a></li><li><a href="#global-interpreter-lock">Global Interpreter Lock</a></li><li><a href="#starting-threads">Starting Threads</a></li><li><a href="#waiting-for-threads">Waiting for Threads</a></li><li><a href="#thread-pool">Thread Pool</a></li><li><a href="#threadpoolexecutor">ThreadPoolExecutor</a></li><li><a href="#synchronization-between-threads">Synchronization between Threads</a></li><li><a href="#when-to-use-threads">When to use Threads</a></li><li><a href="#multithreading-vs-multiprocessing">Multithreading vs Multiprocessing</a></li><li><a href="#why-not-use-asyncio">Why Not Use Asyncio</a></li><li><a href="#python-multithreading-best-practices">Python Multithreading Best Practices</a></li></ol><h2 id="what-is-a-thread">What is a Thread?</h2><p>Imagine that you have a bunch of tasks that need to be done, like making a sandwich, doing your homework, and playing a video game. You can only do one thing at a time, so you have to decide what to do first.</p><p>Now imagine that you have a friend who can help you with some of these tasks. You can both work on different tasks at the same time, which means you can get everything done faster.</p><p>This is similar to how threads work in a computer. The computer has a lot of tasks that it needs to do, and it can only do one thing at a time. But it can create little helpers called threads that can work on different tasks at the same time, which makes the computer faster and more efficient.</p><p>A thread is like a separate mini-program that runs within the main program. Each thread has its own set of instructions to follow and its own memory space, which means it can perform a specific task independently of the other threads within the same program.</p><p>Threads are helpful because they allow a program to do multiple things at the same time. For example, a web browser might use threads to download a file from the internet while also displaying a progress bar and responding to user input.</p><p>The operating system (which is the software that runs on a computer and manages the hardware) creates and manages threads. It gives each thread a priority (which determines how much work it gets to do) and makes sure that all the threads get a fair share of the available resources.</p><p>A Python thread is just a reference to the native thread provided by the underlying operating system.</p><h2 id="threads-and-processes">Threads and Processes</h2><p>Every program is a process and has at least one thread that executes instructions for that process. When we run a Python script, it starts a new Python process that runs our code in the main thread.</p><p>A thread is a lightweight subprocess, meaning that it runs within the process of a program and shares the same memory space. This allows multiple threads to run concurrently within a single process and can make a program more efficient by allowing different parts of the program to run at the same time.</p><p>The Python process will terminate once all (non-background threads) are terminated.</p><p>For more information about processes and when to use them instead of threads, see <a href="https://www.hackerculture.com/running-processes-python-multiprocessing-guide/">Running Multiple Processes in Python - A Multiprocessing Guide.</a></p><h2 id="global-interpreter-lock">Global Interpreter Lock</h2><p>In Python, the Global Interpreter Lock (aka GIL) is a mechanism that prevents multiple native threads from executing Python code at once.</p><p>This lock is necessary because the Python interpreter is not thread-safe, which means that multiple threads can potentially interfere with each other and cause <em>race conditions</em>.</p><p>The GIL makes it easy to write simple, thread-safe Python programs, but it can also limit the performance of multithreaded programs, especially those that are heavily CPU-bound. This is because the GIL prevents multiple threads from executing Python code simultaneously, even if the CPU has multiple cores and could potentially run the threads in parallel.</p><p>This means that threads in Python are not truly parallel and instead run in a concurrent manner but only one at a time.</p><p>So while threads cannot run in parallel, they can run concurrently, which means that more than one task can be in progress at the same time, even if they are not executing at the same time. Once a pending thread can be resumed, Python will switch to executing that thread while the others are still in progress.</p><h2 id="starting-threads">Starting Threads</h2><p>Threads can be started in a number of ways. The most common way to start a thread is to use the <strong>threading</strong> module in Python. This module provides a number of classes and functions that can be used to start, control, and manage threads.</p><p>Given this function which we want to execute in a separate thread from the main program:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">def thread_function():
    print(&quot;Thread function executing&quot;)
    # Simulate a long task
    sleep(1)
</code></pre><figcaption>A function that we want to run in a separate thread</figcaption></figure><p>We can create a new Thread and specify the function as the target argument in the constructor:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># Create a new thread and start it
thread = Thread(target=thread_function)
thread.start()
</code></pre><figcaption>Create and start a new thread with a target function</figcaption></figure><p>The complete example of executing the function in a separate thread:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">from threading import Thread

def thread_function():
    print(&quot;Thread function executing&quot;)
    # Simulate a long task
    sleep(1)

if __name__ == &quot;__main__&quot;:
    # Create a new thread and start it
    thread = Thread(target=thread_function)
    thread.start()

    # The main program continues concurrently with the new thread
    print(&quot;Main program continuing to execute&quot;)
</code></pre><figcaption>Start a new Thread to execute a function</figcaption></figure><p>After the thread is started, the main program continues to execute concurrently with the new thread. The two threads will run concurrently until the main program finishes or until the new thread is stopped or terminated.</p><h3 id="starting-threads-with-arguments">Starting threads with arguments</h3><p>What if we have a function that takes arguments in order to run? Let&apos;s update our example to demonstrate how to run a thread with a target function with arguments.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">def thread_function(greet, seconds):
    print(greet)
    # Simulate a long task
    sleep(seconds)
</code></pre><figcaption>A target function with arguments</figcaption></figure><p>We can specify the arguments to the target function in the Thread constructor by setting the <code>args</code> parameter to a tuple of arguments.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># Create a new thread and start it
thread = Thread(target=thread_function, args=(&quot;Hello from thread&quot;, 3))
thread.start()
</code></pre><figcaption>Pass the target function arguments to the Thread constructor</figcaption></figure><h3 id="creating-daemon-threads">Creating daemon threads</h3><p>The thread constructor also accepts a <code>daemon</code> argument which can be set to <code>True</code> and indicates that we don&apos;t need to wait until this thread finishes. These types of threads may be shut down abruptly once the main thread finishes.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># Create a new daemon thread
thread = Thread(target=thread_function, daemon=True)
thread.start()
</code></pre><figcaption>Setting the daemon argument of a thread</figcaption></figure><p>Daemon threads are typically used for tasks that do not need to be completed before the program exits, such as cleaning up resources or performing periodic maintenance. Also, for tasks that are not related to the main program.</p><p>For example, deleting some temp files if you don&#x2019;t need them anymore may take some time, and we don&apos;t need to wait around to know when. Or perhaps some database records to be deleted and also their associated resources. Those can be scheduled in a separate daemon thread.</p><p>They are not intended for long-running tasks or tasks that need to be completed before the main program exits.</p><h2 id="waiting-for-threads">Waiting for Threads</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.hackerculture.com/content/images/2023/01/Threading-diagram-1.png" class="kg-image" alt="Threads in Python - The Multithreading Guide" loading="lazy" width="390" height="281"><figcaption>Use thread.join() method to wait for a thread to finish</figcaption></figure><p>When we need to wait for another thread to finish executing, we can call the <code><strong>join()</strong></code> method. This is not always needed, as the main thread will not exit until the other threads have finished executing or when only daemon threads are left. But when you need to ensure a thread has completed its task, use <code>join()</code>.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># Wait for the thread to finish
thread.join()
print(&quot;The thread has completed&quot;)</code></pre><figcaption>Waiting for threads to finish executing</figcaption></figure><p>The main program will pause at this point until the thread finishes execution. Once the thread finishes, the main program continues and executes the statement after the <code>join()</code> call returns.</p><h3 id="how-to-set-a-thread-timeout">How to set a thread timeout</h3><p>It is generally a good practice to use timeouts when joining threads in Python because it allows you to specify a maximum amount of time to wait for the thread to finish. This can be especially useful in situations where you are running multiple threads concurrently, and you want to ensure that your program does not get stuck waiting for a thread that may never complete.</p><p>The <code>join()</code> method accepts an optional <code>timeout</code> argument to set the maximum amount of seconds to block when waiting for a thread to finish. Once the timeout is up, the thread will not block anymore, and we can call the <code>is_alive()</code> method to check if a timeout happened - if the thread is still alive, the <code>join()</code> call timed out.</p><p>Here&apos;s an example:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># Wait 10 seconds for the thread to finish
thread.join(timeout=10)
if thread.is_alive():
    print(&quot;The thread timed out&quot;)
else:
    print(&quot;The thread has completed&quot;)</code></pre><figcaption>Use timeouts to set the number of seconds to wait for a thread</figcaption></figure><h2 id="thread-pool">Thread Pool</h2><p>A <strong>thread pool</strong> is a group of worker threads that are kept in a pool and are available to perform tasks as needed. Thread pools are used to improve the performance and scalability of programs that perform a lot of tasks concurrently.</p><p>So in our example before, instead of having a single friend who can help you with your tasks, imagine that you have a group of friends who can help. </p><p>Each of your friends is a worker thread, and they are all part of your thread pool. When you have a task that needs to be done, you can ask one of your friends to help you with it. Once they finish the task, they go back to the thread pool and wait for the next task.</p><p>Using a thread pool can help to improve the performance of a program because the threads can be reused, which means that the program doesn&apos;t have to create a new thread every time it needs to perform a task. This can save time and resources.</p><p>Let&apos;s see an example of how to use a <code>ThreadPool</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">from multiprocessing.pool import ThreadPool

def task_function(x):
    print(f&quot;Calculating square of {x} in thread.&quot;)
    return x**2

if __name__ == &quot;__main__&quot;
    with ThreadPool() as pool:
        numbers = [1, 3, 5, 8, 13]
        result = pool.map(task_function, numbers)
        print(result)</code></pre><figcaption>Starting a ThreadPool to run multiple tasks concurrently</figcaption></figure><p>In this example, a <code>ThreadPool</code> is used to create and start threads to run the target function for each number in the <code>numbers</code> list.</p><p>We can specify an argument to limit how many worker threads to use. If we leave this out, Python creates as many threads as the number of processors in the machine, which is obtained by calling <code>os.cpu_count()</code>.</p><p>Let&apos;s visualize a single thread running a task vs using multiple threads. Note how using more threads brings execution time down:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lh5.googleusercontent.com/QbrsFnchuRloQOOpcXPBKNUudoXMpnc68GODQShh49N1vcu8mRzkZCcdt8oyq7pOHALs3uM8qRMGbkwjt-jyjOhfsQSh7tM7snxV0L5wpmHoxJes6bi5vBwHQVJhwW-RfMUuzKfyial56tpwHyk926BI296QwBhNgWLM0x3Y49TGYi4XBJIndTE-CaP_IQ" class="kg-image" alt="Threads in Python - The Multithreading Guide" loading="lazy" width="404" height="286"><figcaption>A single thread running sequential tasks</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://lh3.googleusercontent.com/JX9dyKvKEgoHB-Sjbd2j2JjJC2Gpj_ysNs3IpuXVDz1GhHBgoEjxyBQUfZs-9usjx9XiybZ60djRTonGoUJVbX95KFnWef8PuvCp37j-eniitMDyhGf3xmwFoKhOGOgqQIOz3AVbhEsEpIMIhlRXy7W2O3IjFrFlAa95oPXVoXenVp50srikw0-pJ-w47g" class="kg-image" alt="Threads in Python - The Multithreading Guide" loading="lazy" width="408" height="272"><figcaption>Two threads running concurrent tasks</figcaption></figure><p>Thread pools are no more than a wrapper around the <strong>threading</strong> module, and in general, we should prefer to use the <code>ThreadPoolExecutor</code>, which has a simpler interface and better compatibility with many other libraries, including <strong><a href="https://www.hackerculture.com/python-asyncio-guide-to-asynchronous-programming/">asyncio</a></strong>.</p><h2 id="threadpoolexecutor">ThreadPoolExecutor</h2><p><code>ThreadPoolExecutor</code> is a class that can be used to create and manage a thread pool. It is part of the <code>concurrent.futures</code> module, which provides a high-level interface for executing tasks concurrently using threads or processes.</p><p>A <code>ThreadPoolExecutor</code> manages a fixed number of worker threads and a task queue. When a task is submitted to the executor, it is placed in the task queue, and a worker thread is assigned to perform the task when one becomes available. The worker thread then executes the task and returns the result (if any) to the caller.</p><p>A <code>ThreadPoolExecutor</code> differs from a simple thread pool in that it provides a more feature-rich interface for managing and executing tasks concurrently. For example, it allows you to:</p><ul><li>Submit tasks asynchronously using <code>Future</code> objects, which can be used to track the progress of the task and retrieve the result when it is completed.</li><li>Set a maximum number of worker threads and a maximum size for the task queue to control the level of concurrency.</li><li>Handle exceptions and cancel tasks that have not yet started.</li></ul><p>Here is a simple example of how to use the <code>ThreadPoolExecutor</code> class to execute a task concurrently in Python:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">from concurrent.futures import ThreadPoolExecutor

def task_function(n):
    print(f&quot;Task {n} running&quot;)

if __name__ == &quot;__main__&quot;:
    # Create a ThreadPoolExecutor with 2 worker threads
    with ThreadPoolExecutor(max_workers=2) as executor:
        # Submit a task to the executor
        future = executor.submit(task_function, 1)

        # The main program continues to execute concurrently with the task
        print(&quot;Main program continuing to execute&quot;)
</code></pre><figcaption>Using a ThreadPolExecutor to run a function concurrently</figcaption></figure><p>In this example, the <code>task_function</code> is a simple function that prints a message to the console. The <code>ThreadPoolExecutor</code> is created with a maximum of 2 worker threads, which means that at most 2 tasks can be executed concurrently.</p><p>Since Python 3.5, if we don&apos;t specify the number of <code>max_workers</code>, it will default to the number of processors on the machine <strong>multiplied by 5.</strong></p><p>To submit a task to the executor, you can use the <code>submit</code> method and pass in the function and any arguments that the function requires. This returns a <code>Future</code> object, which represents the result of the task and can be used to track the progress of the task and retrieve the result when it is completed.</p><p>We can also use a <code>ThreadPoolExecutor</code> as a simplified version of the <code>ThreadPool</code> we discussed earlier:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">from time import sleep
from random import random
from concurrent.futures import ThreadPoolExecutor

def target_function(name):
    # sleep for less than a second
    sleep(random())
    return name
 
# start the thread pool
with ThreadPoolExecutor(10) as executor:
    # execute tasks concurrently and process results in order
    for result in executor.map(target_function, range(10)):
        # retrieve the result
        print(result)</code></pre><figcaption>Using ThreadPoolExecutor to map a task to a list of inputs</figcaption></figure><p>You may be interested in the results of the <code>ThreadPoolExecutor</code> as they become available instead of once all the tasks have been completed. Note that the order in which tasks are completed is not guaranteed.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">from concurrent.futures import as_completed
...

# start the thread pool
with ThreadPoolExecutor(10) as executor:
    # submit tasks and collect futures
    futures = [executor.submit(target_function, i) for i in range(10)]
    # process task results as they are available
    for future in as_completed(futures):
        # retrieve the result
        print(future.result())</code></pre><figcaption>Using the results as they become available</figcaption></figure><h3 id="what-are-futures"><strong>What Are Futures</strong></h3><p>A <strong>Future</strong> object acts as a placeholder for the result of a task that has not yet completed. It allows you to track the progress of the task and retrieve the result when it is completed.</p><p>It is also sometimes called a <em>promise</em> or a <em>delay</em>. It provides a context for the result of a task that may or may not be executing and a way of getting a result once it is available.</p><p>In Python, the <strong><strong>Future</strong></strong> object is returned from an <strong><strong>Executor</strong></strong>, such as the <strong><strong><code>ThreadPoolExecutor</code>,</strong></strong> when calling the <code><strong>submit()</strong></code> method to dispatch a task to be executed asynchronously.</p><p>A <strong><strong>Future</strong></strong> also provides access to the result of the task via the <strong><strong><code>result()</code></strong></strong> method. If an exception was raised while executing the task, it will be re-raised when calling the <code>result()</code> method or can be accessed via the <strong><strong><code>exception()</code></strong></strong> method.</p><ul><li><strong><strong><code>result()</code></strong></strong>: Access the result from running the task.</li><li><strong><strong><code>exception()</code></strong></strong>: Access any exception raised while running the task.</li></ul><p>Both the <code>result()</code> and <code>exception()</code> methods allow a timeout to be specified as an argument, which is the number of seconds to wait for a return value if the task is not yet complete. If the timeout expires, then a <strong><strong><code>TimeoutError</code></strong></strong> will be raised.</p><h2 id="synchronization-between-threads">Synchronization between threads</h2><p>When multiple threads are running concurrently, it is important to ensure that they do not interfere with each other. This is done through synchronization, which is the process of ensuring that all threads are in sync and are accessing the same data.</p><p>Consider a program that has a shared counter variable that is incremented by multiple threads. If two threads try to increment the counter simultaneously, it is possible that the final value of the counter will be incorrect because the threads may interfere with each other&apos;s updates. This is called a <em>race condition</em>.</p><p>A <strong>race condition</strong> is a concurrency failure case when two threads run the same code and access or update the same resource (e.g. data variables, stream, etc.), leaving the resource in an unknown and inconsistent state.</p><p>Race conditions often result in unexpected behavior of a program and/or corrupt data.</p><p>Try running this example many times, and you will encounter a race condition:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import threading

counter = 0

def increment_counter():
    global counter
    counter += 1

# Create two threads that try to increment the counter simultaneously
t1 = threading.Thread(target=increment_counter)
t2 = threading.Thread(target=increment_counter)

# Start the threads
t1.start()
t2.start()

# Wait for the threads to finish
t1.join()
t2.join()

# Print the final value of the counter
print(counter)</code></pre><figcaption>An example cause of a race condition</figcaption></figure><h3 id="using-threadinglocal">Using threading.local</h3><p>A key difference between processes and threads is that processes have their own separate memory space, while threads share the memory space of the process that created them. This means that threads can communicate with each other more easily, but it also means that if one thread modifies the shared memory, it can affect the other threads in the process.</p><p>One way to make sure threads don&apos;t interfere with each other is to separate the data they operate on, so other threads can&apos;t access them. We can do that using the <code>threading.local</code> class which provides a dictionary-like object that is specific to a thread, and each thread gets its own separate copy of the values stored in the <code>threading.local</code> object.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import threading

# Create a thread-local data object
local_data = threading.local()

def process_request(request):
    # Store the request data in the thread-local data object
    local_data.request = request

    # Do some processing with the request data
    # ...

# Start a new thread and process a request
t = threading.Thread(target=process_request, args=(request,))
t.start()
</code></pre><figcaption>Use threading.local() to separate thread memory space</figcaption></figure><p>In this example, the <code>process_request</code> function stores the request data in the <code>local_data</code> object, which is specific to the thread that is executing the function. This means that the request data will not be shared with any other threads, and each thread can have its own separate copy of the request data.</p><p>We can also separate the data between threads using threading and queue:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import threading
import queue

def worker(q):
    while True:
        item = q.get()
        if item is None:
            break
        # process the item
        print(item)
        q.task_done()

q = queue.Queue()

# add items to the queue
for item in range(100):
    q.put(item)

# create worker threads
threads = []
for i in range(4):
    t = threading.Thread(target=worker, args=(q,))
    t.start()
    threads.append(t)

# wait for all tasks to be completed
q.join()

for t in threads:
    t.join()
</code></pre><figcaption>Python threading and queue example</figcaption></figure><h3 id="locks">Locks</h3><p>A (mutually exclusive) <em>lock</em> can be used to ensure that only one thread at a time executes a critical section of code at a time, while all other threads trying to execute the same code must wait until the currently executing thread is finished with the critical section and releases the lock.</p><p>To avoid a race condition in Python using a lock, we can use the <code>threading.Lock</code> class to create a lock object, and then use the <code>acquire</code> and <code>release</code> methods of the lock to control access to the shared resource.</p><p>Here is an example of how we can use a lock to avoid a race condition in Python:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">from threading import Thread

counter = 0
lock = threading.Lock()

def increment_counter():
    global counter
    with lock:
        # Critical section executed by a single thread at a time
        counter += 1

# Create two threads that try to increment the counter simultaneously
t1 = Thread(target=increment_counter)
t2 = Thread(target=increment_counter)

# Start the threads
t1.start()
t2.start()

# Wait for the threads to finish
t1.join()
t2.join()

# Print the final value of the counter
print(counter)
</code></pre><figcaption>Using locks to prevent a race condition</figcaption></figure><p>In this example, the <code>increment_counter</code> function acquires the lock before accessing and modifying the shared counter variable. This ensures that only one thread can execute the code inside the <code>with</code> block at a time, which prevents the race condition from occurring. As a result, the final value of the counter will always be 2, regardless of the order in which the threads execute.</p><p>Something to keep in mind is that we must ensure the holder releases any acquired locks. Otherwise, other threads may wait indefinitely for access, and our program may never finish. This is called a <em><em>deadlock</em></em>.</p><h2 id="when-to-use-threads">When to Use Threads</h2><p>As we saw earlier, the Python interpreter prevents more than one thread from executing bytecode at the same time.</p><p>This is achieved using a lock called the Global Interpreter Lock or GIL, as we learned in the previous section.</p><p>To bypass the GIL and take full advantage of multiple CPU cores in your Python program, you may want to consider using a different concurrency framework like &#xA0;<a href="https://www.hackerculture.com/running-processes-python-multiprocessing-guide/"><code>multiprocessing</code></a> and also <code>concurrent.futures</code>. These frameworks allow you to create multiple processes, each with its own Python interpreter and GIL, which can run in parallel on separate CPU cores.</p><p>There are other times when the lock is released by the interpreter, and we can achieve parallel execution of our concurrent code in Python.</p><p>Examples of when the lock is released include:</p><ul><li>When a thread is performing blocking IO.</li><li>When a thread is executing C code and explicitly releases the lock.</li></ul><p>This makes threads good candidates for IO-bound tasks.</p><p>An IO-bound task is a type of task that involves reading from or writing to a device, file, or socket connection. These tasks involve input and output (IO), and the speed of these operations is bound by the device, hard drive, or network connection. This is why these tasks are referred to as IO-bound.</p><p>A thread performing an IO operation will block for the duration of the operation. While blocked, this signals to the operating system that a thread can be suspended and another thread can execute, called a context switch.</p><p>Additionally, the Python interpreter will release the GIL when performing blocking IO operations, allowing other threads within the Python process to execute.</p><p>This is why blocking IO operations are an excellent use case for using threads in Python.</p><p>Let&apos;s look at practical examples of blocking IO operations in real applications:</p><p><strong>Reading or writing a file from the hard drive</strong></p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># Open the file for writing
def write_file():
    with open(&apos;filename.txt&apos;, &apos;w&apos;) as f:
        # Write some text to file, could take long...
        f.write(&apos;Local data saved.&apos;)
</code></pre><figcaption>Saving some data in a local file</figcaption></figure><p><strong>Printing a document</strong></p><p>If you want to print the output to a specific device, such as a printer, you can use the <code>print()</code> function in combination with a library that provides printing functionality. For example, you can use the <code>pycups</code> library to print to a CUPS (Common Unix Printing System) printer or the <code>win32print</code> library to print to a printer on a Windows operating system.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import cups

# Connect to the local CUPS server
conn = cups.Connection()

# Get a list of available printers
printers = conn.getPrinters()

# Print a document to the default printer
conn.printFile(printers.keys()[0], &apos;/path/to/file.pdf&apos;, &apos;Title&apos;, {})
</code></pre><figcaption>Printing a document with the cups library</figcaption></figure><p><strong>Downloading or uploading a file</strong></p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import requests

# URL of the file to download
url = &apos;https://www.example.com/file.zip&apos;

def download_url(url):
    # Send an HTTP GET request to the URL
    response = requests.get(url)
    # Check for a successful response
    if response.status_code == 200:
        # Save the content of the response to a file
        with open(&apos;file.zip&apos;, &apos;wb&apos;) as f:
            # Writing to file here is blocking
            f.write(response.content)
</code></pre><figcaption>Downloading a file with the requests library in Python</figcaption></figure><p>This will download the file at the specified URL and save it to the current working directory with the name <code>file.zip</code>.</p><p><strong>Working with APIs</strong></p><p>Here is an example of how you can use the Python <code>requests</code> library to query a real-world API:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import requests

def openweather_api(city=&quot;London&quot;, country_code=&quot;UK&quot;):
    # Set the API endpoint and API key
    api_endpoint = &quot;https://api.openweathermap.org/data/2.5/weather&quot;
    # Set the parameters for the API request
    params = {
        &quot;q&quot;: f&quot;{city},{country_code}&quot;,
        &quot;appid&quot;: &quot;YOUR_API_KEY_HERE&quot;,
    }
    # The request to the API endpoint blocks until we get a response
    response = requests.get(api_endpoint, params=params)
    # Check the status code of the response to check if successful
    if response.status_code == 200:
        # If the request was successful, get the data from the response
        data = response.json()
        # You can now process the data as needed
        print(data)
    else:
        print(&quot;Request failed with status code:&quot;, response.status_code)
        print(&quot;Error message:&quot;, response.text)
</code></pre><figcaption>Making API requests</figcaption></figure><p><strong>Connecting to a database</strong></p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import sqlite3

with sqlite3.connect(&quot;database.db&quot;) as conn:
    # Create a cursor
    cursor = conn.cursor()
    # The execute blocks while connecting to the database
    cursor.execute(&quot;CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)&quot;)
    data=(&apos;Alice&apos;, 25)
    cursor.execute(&quot;INSERT INTO users (name, age) VALUES (?, ?)&quot;, data)
</code></pre><figcaption>Writing to SQLite database</figcaption></figure><p><strong>Taking a photo or recording a video.</strong></p><p>To take a photo or record a video using Python, you will need to use a library or module that provides access to the camera or video capture functionality of your system. One option is the <code>cv2</code> (OpenCV) library:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import cv2

# Open the camera
camera = cv2.VideoCapture(0)

def take_photo(filename=&apos;photo.jpg&apos;)
    ret, frame = camera.read()
    if ret:
        # Save the photo to a file
        cv2.imwrite(filename, frame)

def take_video(filename=&apos;video.mp4&apos;, width=640, height=480)
    fourcc = cv2.VideoWriter_fourcc(*&apos;MP4V&apos;)
    out = cv2.VideoWriter(filename, fourcc, 30.0, (width,height))
    
    while True:
        # Capture a frame from the camera
        ret, frame = camera.read()
        if not ret:
            break
        # Write the frame to the video file
        out.write(frame)
        # Show the frame
        cv2.imshow(&quot;Webcam&quot;, frame)

    # Release the camera and video writer
    camera.release()
    out.release()
    cv2.destroyAllWindows()</code></pre><figcaption>How to take photos and videos from webcam with OpenCV</figcaption></figure><h2 id="multithreading-vs-multiprocessing">Multithreading vs Multiprocessing</h2><p>Threads and Processes are quite different, and choosing one over the other is intentional.</p><p>A Python program is a process that has a main thread. You can create many additional threads in a Python process. You can also fork or spawn many Python processes, each of which will have one thread and may spawn additional threads.</p><p>More broadly, threads are lightweight and can share memory (data and variables) within a process, whereas processes are heavyweight and require more overhead and impose more limits on sharing memory (data and variables).</p><p>Typically, processes are used for CPU-bound tasks, and threads are used for IO-bound tasks, and this is a good heuristic, but this is not always the case. In the real world, we may need to write programs that contain a mix of both IO-bound tasks and CPU-bound tasks.</p><p>In this case, we should experiment with using threading for the IO-bound tasks of our programs and processes or multiprocessing too for the CPU-bound parts. Perhaps try it and see.</p><p>You can learn all about multiprocessing in this guide:</p><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://www.hackerculture.com/running-processes-python-multiprocessing-guide/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Running multiple processes in Python - a multiprocessing guide</div><div class="kg-bookmark-description">Learn how to write Python programs that can run simultaneously on all processors available and also as background processes.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.hackerculture.com/favicon.ico" alt="Threads in Python - The Multithreading Guide"><span class="kg-bookmark-author">Hacker Culture</span><span class="kg-bookmark-publisher">Adrian Cruz</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://images.unsplash.com/photo-1672224745017-a9b54ad9188f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8YWxsfDI5fHx8fHx8Mnx8MTY3MjI0ODY3MQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Threads in Python - The Multithreading Guide"></div></a><figcaption>Running multiple processes in Python - a multiprocessing guide</figcaption></figure><h2 id="why-not-use-asyncio">Why Not Use Asyncio</h2><p>Asyncio is a newer alternative to using threads introduced in Python 3.4 and is probably worth investigating as a replacement for multithreading.</p><p>Asyncio is designed to support large numbers of IO operations, perhaps thousands to tens of thousands, all within a single Thread.</p><p>If you are writing a new program from scratch, you may be better off using asyncio instead of threads.</p><p>On the other hand, if you are working with an existing codebase containing legacy code or targeting an older version of Python, your best bet is to use threads.</p><p>Simply put, threads should be considered only when working with legacy code if you need to speed up some IO-bound tasks in an established code base.</p><p>You can learn all about asyncio in python with our guide:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.hackerculture.com/python-asyncio-guide-to-asynchronous-programming/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Python asyncio - A Guide to Asynchronous Programming</div><div class="kg-bookmark-description">In this guide, we&#x2019;ll introduce asynchronous programming in Python and review fundamental concepts like how to define, create and run asynchronous functions, coroutines, and some common use cases and best practices.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.hackerculture.com/favicon.ico" alt="Threads in Python - The Multithreading Guide"><span class="kg-bookmark-author">Hacker Culture</span><span class="kg-bookmark-publisher">Adrian Cruz</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.hackerculture.com/content/images/2023/01/olaf-ZT3mEdrBS7Y-unsplash.webp" alt="Threads in Python - The Multithreading Guide"></div></a></figure><h2 id="python-multithreading-best-practices">Python Multithreading Best Practices</h2><p>Now that we know threads work and how to use them let&#x2019;s review some best practices to consider when bringing threads into our Python programs.</p><p>Best practices to keep in mind when using threads in Python:</p><ol><li>Use Context Managers</li><li>Use Timeouts When Waiting</li><li>Use Locks to Prevent Race Conditions</li><li>Consider Using <a href="https://www.hackerculture.com/python-asyncio-guide-to-asynchronous-programming/">Asyncio</a></li></ol><h2 id="references">References</h2><ul><li><a href="https://docs.python.org/3/library/threading.html?ref=hackerculture.com">threading module</a></li><li><a href="https://docs.python.org/3/library/concurrent.futures.html?ref=hackerculture.com#concurrent.futures.ThreadPoolExecutor">ThreadPoolExecutor</a></li><li><a href="https://amzn.to/3GxtZuJ?ref=hackerculture.com">Fluent Python Book</a></li><li><a href="https://superfastpython.com/threading-in-python/?ref=hackerculture.com">Python Threading: The Complete Guide</a></li></ul><p><strong><strong>Did you find this guide helpful?</strong></strong><br>If so, I&apos;d love to know. Please share what you learned in the comments below.</p><p><strong><strong>How are you using </strong>threads<strong> in your programs?</strong></strong><br>I&#x2019;d love to hear about it, too. Please let me know in the comments.</p><p><strong><strong>Do you have any questions?</strong></strong><br>Leave your question in a comment below, and I&apos;ll answer it with my best advice.</p>]]></content:encoded></item><item><title><![CDATA[Why I Started the Atomic-fns Library]]></title><description><![CDATA[Adopting open-source projects doesn't cost any money, but there are more subtle costs that we should consider before introducing any dependencies to existing products or processes.]]></description><link>https://www.hackerculture.com/introducing-atomic-fns-library/</link><guid isPermaLink="false">63613610bc22b7003dacae54</guid><category><![CDATA[Open Source]]></category><category><![CDATA[Opinion]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Sat, 05 Nov 2022 15:00:11 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/norbert-kowalczyk-jrKKj9nJMxM-unsplash.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/norbert-kowalczyk-jrKKj9nJMxM-unsplash.webp" alt="Why I Started the Atomic-fns Library"><p>Open source is more important than ever, and more companies are realizing the benefits and increasing their adoption. As long as it&apos;s something popular that can speed up the development process, why not use it? It doesn&apos;t cost anything!</p><p>Well, it doesn&apos;t cost <em>money</em>... But I&apos;ve found there are more subtle costs that could end up having a huge impact on the business, and we should consider them before introducing any dependencies to existing products or processes.</p><p>There&apos;s the cost of distributing the libraries you&apos;re using (and their dependencies) to your customers in your builds. That means your customers need to download a larger bundle, which makes their experience a bit slower.</p><p>There&apos;s the maintenance cost of outdated libraries, which often include outdated dependencies. Those could have known vulnerabilities with known exploits (and fixes), which can damage the brand, business, and customers.</p><p>When a library is no longer maintained, it&apos;s almost impossible to get support or request new features. The issues pile up on an endless list of open bugs. And those bugs are almost never fixed when you need them to be. You can count on Murphy&apos;s Law to kick in here.</p><p>Then there&apos;s the cost of the workarounds. Once you start uncovering bugs and you realize nobody is coming to save you, there are two possible outcomes: you either stop using that library, or you try to make it work on your own, taking on weeks of additional work.</p><p>On the other hand, there are specific characteristics that separate the quality open-source projects from their abandoned counterparts:</p><ul><li><strong>Bundle sizes.</strong> Good projects are modular, so you can import only what you need and keep your bundle sizes low. JavaScript libraries can be checked using <a href="https://bundlephobia.com/?ref=hackerculture.com">bundlephobia.com</a> &#x2013; also, look for libraries that are <a href="https://www.smashingmagazine.com/2021/05/tree-shaking-reference-guide/?ref=hackerculture.com">tree-shakeable</a>.</li><li><strong>Source code.</strong> If you aren&apos;t able to inspect the code, <em>don&apos;t use it because it could explode</em>. But also, is the code clear? Are there comments? Could you explain how it works to others?</li><li><strong>Up to date.</strong> Look at the commit history. Are there recent commits and bug fixes landed? Are they keeping their dependencies up to date?</li><li><strong>Test coverage.</strong> If there are no tests, how do we know the code does what it&apos;s supposed to do? Browse through the tests and make sure that they cover enough cases.</li><li><strong>Open issues.</strong> Check the project&apos;s open issues to make sure the maintainers are active and listening. Check for a roadmap too.</li><li> <strong>No vulnerabilities.</strong> Run <code>npm audit</code> after adding a new dependency to check that it doesn&apos;t introduce vulnerabilities. Bonus points if the library you plan to use has zero dependencies.</li><li><strong>Documentation.</strong> Can you find available docs that show how the library is meant to be used? Official or otherwise, having something you can refer to and share with other team members is super valuable.</li></ul><p>Recently, I&apos;ve had to estimate these costs when deciding on which libraries to use for a new project and was considering using the popular <code>lodash</code> library. Until I started looking at their long list of <a href="https://github.com/lodash/lodash/issues?ref=hackerculture.com">open bugs</a>, realizing this project has been dead for the past 6 years, and then I read their source code, ridden with custom macros and over-engineered abstractions (see <a href="https://github.com/lodash/lodash/blob/master/clone.js?ref=hackerculture.com">clone</a>). And then it hit me...</p><p>This must be why there are so many open bugs. It takes so much effort to understand just one basic function that most people won&apos;t even try. Hell, even the original maintainers probably can&apos;t understand it at this point because of all the abstractions.</p><p>So I started working on a <a href="https://github.com/adrian2x/atomic-fns?ref=hackerculture.com">similar library</a> to replace my <code>lodash</code> imports, and a handful of other libraries. But the main goal is that it should be easy to learn, easy to test, and easy to maintain. There are plenty of <a href="https://atomic-fns.dev/?ref=hackerculture.com">docs</a> available to get you started, and I hope you join the conversation on <a href="https://github.com/adrian2x/atomic-fns?ref=hackerculture.com">github</a> to help shape its future.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/adrian2x/atomic-fns?ref=hackerculture.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - adrian2x/atomic-fns: Like Lodash, but for ESNext and with types.</div><div class="kg-bookmark-description">Like Lodash, but for ESNext and with types. Contribute to adrian2x/atomic-fns development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Why I Started the Atomic-fns Library"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">adrian2x</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/91028d389e69e00d4f5a284ae39e5af0d08cec7b20c84e30e4ed6d3f5c3bdaf5/adrian2x/atomic-fns" alt="Why I Started the Atomic-fns Library"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[A Gentle Introduction to Python Programming]]></title><description><![CDATA[Learn how to set up Python, what a computer program is, how to write programs, how to create variables to name values, and how to do basic math operations.]]></description><link>https://www.hackerculture.com/intro-to-python/</link><guid isPermaLink="false">63c97998df5713004d80db7c</guid><category><![CDATA[Beginner]]></category><category><![CDATA[Python]]></category><category><![CDATA[101]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Wed, 05 Oct 2022 14:00:00 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/ales-nesetril-Im7lZjxeLhg-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/ales-nesetril-Im7lZjxeLhg-unsplash.jpg" alt="A Gentle Introduction to Python Programming"><p>Programming offers a chance to develop creativity, reasoning, and problem-solving skills. As a programmer, you get to bring ideas to life through code and using logic to create programs that can be executed by a computer.</p><p>If you know how to write computer programs, you can do all sorts of useful things. You might not be able to write programs to control cars, traffic lights, or your fridge right away, but you could make web pages, your own games, or even a program to help you with your homework.</p><p>When faced with obstacles, programming requires you to use problem-solving to find solutions. While it can be challenging (and at times frustrating), programming is also a fun and rewarding activity! The skills gained from programming can be beneficial in multiple professions, even if your chosen career is not directly related to computers.</p><h2 id="how-to-learn-to-code">How to Learn to Code</h2><p>Like anything you try for the first time, it&#x2019;s always best to start with the basics. Gymnasts don&apos;t usually do backflips on their first try. If you jump ahead too quickly, not only will you easily forget the basics, but you&#x2019;ll also find the content of the later articles more complicated than it actually is.</p><p>Remember that the better you understand the basics, the easier it will be to understand more complicated ideas later on. When you find something frustrating or too challenging, here are some things that I find helpful:</p><ol><li>Break a problem down into smaller pieces. Try to understand what a small piece of code is doing or think about only a small part of a difficult idea (focus on a small piece of code rather than trying to understand the whole thing at once).</li><li>If that still doesn&#x2019;t help, it might be best to leave it alone for a while. Sleep on it and come back to it another day. This is a good way to solve many problems, and it can be particularly helpful for computer programmers.</li></ol><h2 id="what-is-a-computer-program">What is a Computer Program</h2><p>A <strong>program</strong> is a list of steps for a computer to follow. <em>Programs</em> are made up of lines of code. Each line tells the computer something specific about how to follow these instructions.</p><p><em>Software</em> is a collection of <em>computer programs</em>.</p><p>Programs are like thoughts. If you didn&#x2019;t have thoughts, you would probably just sit on the couch all day, staring at the ceiling. Your thought &#x201C;get up off the couch&#x201D; is a command or instruction that tells your body to stand up. In the same way, computer programs use commands to tell computers what to do.</p><h3 id="the-python-programming-language">The Python Programming Language</h3><p>Like humans, computers use multiple languages to communicate&#x2014;these are called programming languages. A <em>programming language</em> is simply a way to talk to a computer by using instructions that both humans and computers can understand.</p><p>The Python programming language has many features that make it extremely useful for beginners. Most importantly, you can use Python to write simple, efficient programs quite quickly. Python doesn&#x2019;t use as many complicated symbols as other programming languages, which makes it easier to read and a lot friendlier for beginners.</p><blockquote>The Python programming language was actually named after the <em>Monty Python&#x2019;s Flying Circus</em> TV show, not after the snake.</blockquote><h2 id="installing-python">Installing Python</h2><p>To install Python, navigate to <a href="https://www.python.org/downloads/?ref=hackerculture.com">python.org/downloads</a> and click on <strong>Download Python</strong>. At the time of this writing, the latest version is 3.11.1. Make sure you download a version higher than 3.10, which is faster than previous versions.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.hackerculture.com/content/images/2023/01/Screenshot-2023-01-19-at-1.22.09-PM.png" class="kg-image" alt="A Gentle Introduction to Python Programming" loading="lazy" width="1009" height="559" srcset="https://www.hackerculture.com/content/images/size/w600/2023/01/Screenshot-2023-01-19-at-1.22.09-PM.png 600w, https://www.hackerculture.com/content/images/size/w1000/2023/01/Screenshot-2023-01-19-at-1.22.09-PM.png 1000w, https://www.hackerculture.com/content/images/2023/01/Screenshot-2023-01-19-at-1.22.09-PM.png 1009w"><figcaption>www.python.org/downloads</figcaption></figure><p>Once downloaded, double-click the file to run the installer.</p><p>Make sure you select <strong>Add Python to PATH</strong>, and accept all the defaults as you click through the installer.</p><p>When the setup is complete, you will find your Python folder inside the <strong>Applications</strong> folder on your computer.</p><p>Now that you have Python installed, we can write our first program!</p><h3 id="for-windows">For Windows</h3><p>Search for the <strong>Command</strong> application in the <strong>Start Menu</strong> or the Windows search box.</p><p>Enter <code>python</code> in all lowercase. You should see a message from Python like this:</p><pre><code class="language-bash">$ python
Python 3.11.0 (main, Jan  9 2023, 15:58:34) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&gt;&gt;&gt; </code></pre><p>Here, you can play around with the Python interpreter. Note the three greater-than signs (<code>&gt;&gt;&gt;</code>) are called the <em>prompt</em>. This means Python is waiting for our input.</p><h3 id="for-mac">For Mac</h3><p>Python is already installed on most Macs, but it&apos;s probably an outdated version and not the one you want.</p><p>To see which version of Python is installed, open the <strong>Terminal</strong> application from the <strong>Applications</strong> folder and enter <code>python3</code>. You should then see the Python version and prompt.</p><p><strong>Note:</strong> A terminal (or <em>shell</em>) is a command-line interface on a computer where you can type commands to the operating system and receive text output in return.</p><h2 id="your-first-python-program">Your First Python Program</h2><p>Now that you have Python installed, we can start writing some Python code. Enter the following line into the prompt:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">print(&quot;Hello World&quot;)</code></pre><figcaption>Your first Python program</figcaption></figure><p>You should see the &quot;Hello World&quot; line printed out!</p><p>To close the terminal session, press <strong>Ctrl-Z</strong> and <strong>Enter</strong>, or type <code>exit()</code> and press <strong>Enter</strong>.</p><p>But wait a second... you may be thinking, &quot;<em>How is this useful?</em>&quot;. That&apos;s a totally fair question. Let&apos;s look at a more realistic example.</p><p>Copy and paste the following code into your Python shell, and press <strong>Enter</strong>. Or try clicking the <strong>Run</strong> button in this sandbox:</p><!--kg-card-begin: html--><iframe src="https://pythonsandbox.dev/embed/grc0q2ztoice?file=main.py" width="800px" height="547px" allow="clipboard-read; clipboard-write"></iframe><!--kg-card-end: html--><p></p><p>You should now see today&apos;s date and time printed out!</p><p><strong>Note:</strong> In Python, the hash symbol (<code>#</code>) indicates a comment. Anything following a hash char in your code is ignored by Python; they are just there for human readers. You can use comments to write notes in plain English within your programs.</p><p>Comments can also be used to experiment with your code. If you want to temporarily &quot;turn off&quot; some code so that it doesn&apos;t run but don&apos;t want to delete it entirely, start the line with a <code>#</code>. This will convert the line into a comment, which Python will ignore when running the program.</p><p>Let&apos;s take a look at the code starting with this line:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">from datetime import datetime</code></pre><figcaption>import the datetime module</figcaption></figure><p>This imports a module called <code>datetime</code> from <em>somewhere</em>, which we will use to display the current date and time. For now, you probably have no idea where it&apos;s coming from, and that&apos;s completely okay.</p><p>The next line is a bit different:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">today = datetime.now()</code></pre><figcaption>Create a new variable called &quot;today&quot;</figcaption></figure><p>It kind of sounds like this line is saying: <code>today is equal to a datetime now</code>, maybe? Or more like <code>today <em>is</em> datetime now</code>.</p><p>That&apos;s pretty much exactly what this line does:</p><ol><li>It creates a new <em>variable</em> name called &quot;<strong>today</strong>&quot;.</li><li>It assigns the result of <code>datetime.now()</code> to the variable <code>today</code>.</li></ol><figure class="kg-card kg-code-card"><pre><code class="language-Python">print(today)</code></pre><figcaption>Print the contents of the variable &quot;today&quot;</figcaption></figure><p>Finally, this prints out the value of the variable <code>today</code>.</p><p>Now, try to change the name <code>today</code> in the last line to something else, like <code>now</code>, and run the code again.</p><h3 id="what-to-do-if-you-see-an-error">What to do if you see an error</h3><p>You may see an error this time, and the output is just the Python interpreter telling us where the problem is.</p><figure class="kg-card kg-code-card"><pre><code>Traceback (most recent call last):
  File &quot;/var/shared-data/code/main.py&quot;, line 6, in &lt;module&gt;
    print(now)
          ^^^
NameError: name &apos;now&apos; is not defined. Did you mean: &apos;pow&apos;?</code></pre><figcaption>An example of a traceback when an error occurs</figcaption></figure><p>A <code>NameError</code> happens when we either forget to set a variable&apos;s name before using it or we made a spelling error when entering the variable name.</p><p>The output also reports that this error happened in line 6 of the file <em>main.py</em>. This can help us spot the error quickly.</p><p>If we change both of these lines to use the same name &quot;now&quot;, the code works again. Try copying these lines into your shell now:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">now = datetime.now()
print(now)</code></pre><figcaption>Fixing the NameError from above</figcaption></figure><p>Many programming mistakes are simple typos of one character in one line of code. If you spend a long time trying to find one of these mistakes, you&apos;re not alone. Many skilled and experienced programmers spend hours looking for these types of small mistakes. Try to laugh about it and move on, knowing that this will happen a lot as you learn to code.</p><h2 id="variables-and-operations">Variables and Operations</h2><p>Variables are a way of storing things in a program. They&apos;re essentially a way to name a value, and they point to the given value in memory. Their value can later be retrieved and used in expressions.</p><p>To create a variable, we simply type the name on the left side of an equal sign (<code>=</code>). The right side must be an expression. This can be a primitive value or a more complicated expression.</p><p>For example, we can assign the result of a calculation to a variable:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">weeks = 52
days = 5 * 52
vacation = 5 * 2 + 4
workdays = days - vacation
workweeks = workdays / 7
print(workdays)</code></pre><figcaption>Basic Python Operators</figcaption></figure><p>To find the value of a variable, we can use <code>print</code> followed by the name of the variable in parentheses. Try copying the code above to your shell, and then press <strong>Enter</strong>.</p><p>We can also tell Python to change the variable so that it contains a new value and also use more than one name or variable for the same value:</p><pre><code class="language-Python">days = 7
week = days
print(week)</code></pre><p>Here we&apos;re changing the value of the variable <code>days</code> to <code>7</code> and then, we&apos;re saying that we want the variable <code>week</code> to refer to the current value of the variable <code>days</code>.</p><p>However, <code>week</code> isn&apos;t a very useful name for a variable because it doesn&apos;t tell us much about what the variable is used for. Let&apos;s call our variable <code>days_in_week</code> instead.</p><pre><code class="language-Python">days_in_week = 7
print(days_in_week)</code></pre><p>This makes it clear that we&apos;re talking about the number of days in a week. Variable names can be made up of letters, numbers, and underscores (<code>_</code>), but they can&apos;t start with a number.</p><p>You can use any lowercase letters for variable names, but the name cannot contain a space, so we use underscores to separate words. </p><p>You should not use uppercase letters in variable names because they have a special meaning that we&apos;ll discuss another time. Python is a case-sensitive language, which means that <code>today</code> is not the same as <code>Today</code>.</p><h3 id="the-order-of-operations">The order of operations</h3><p>You can do all the basic math operations in Python using the corresponding <em>operators,</em> as you saw in that example.</p><p>We use parentheses in programming languages to control the order of operations. An operation is anything that uses an operator. Multiplication and division have a higher order than addition and subtraction, so they&#x2019;re performed first. In other words, if you enter an equation in Python, multiplication or division is performed before addition or subtraction.</p><p>For example, in this expression, the numbers <code>2</code> and <code>10</code> are multiplied first, and then the number <code>5</code> is added to the product:</p><pre><code>5 + 2 * 10</code></pre><p>The result is <code>25</code>.</p><p>We can change the order of operations (and the result) by adding the parentheses around the numbers:</p><pre><code>(5 + 2) * 10</code></pre><p>The result of this expression is now <code>70</code> because the parentheses tell Python to add <code>5</code> to <code>2</code>, and then multiply the result by <code>10</code>.</p><p>Parentheses can be nested, which means that there can be parentheses inside parentheses, like this:</p><pre><code>((5 + 2) * 10) / 7</code></pre><p>In this case, Python evaluates the innermost parentheses first, then the outer ones, and then the final division. So it adds <code>5</code> to <code>2</code>, then multiplies the result by <code>10</code>, and then divides that by <code>7</code>.</p><h2 id="saving-your-python-programs">Saving Your Python Programs</h2><p>As we saw, we can write Python code directly into the shell window... but Python programs wouldn&#x2019;t be very useful if you needed to rewrite them every time you wanted to use them.</p><p>Sure, it might be fine for short programs, but a large program could have millions of lines of code!</p><p>Luckily, we can save our programs for future use.</p><p>To create and save a new program, enter <strong>idle</strong> in the Windows search box, and select <strong>IDLE (Python 3.11.1 64-bit)</strong>. In a Mac, open your <strong>Applications</strong> folder and open the <strong>Python 3.11</strong> folder to find IDLE.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.hackerculture.com/content/images/2023/01/image-5.png" class="kg-image" alt="A Gentle Introduction to Python Programming" loading="lazy" width="667" height="313" srcset="https://www.hackerculture.com/content/images/size/w600/2023/01/image-5.png 600w, https://www.hackerculture.com/content/images/2023/01/image-5.png 667w"><figcaption>The IDLE python shell</figcaption></figure><p>IDLE (<strong>I</strong>ntegrated <strong>D</strong>evelopment &amp; <strong>L</strong>earning <strong>E</strong>nvironment) is a Python file editor which lets you write programs in Python.</p><p>When you open IDLE, you will see the Python shell. Click <strong>File &#x203A; New Window</strong>. An empty window will appear. </p><p>Paste the code from the previous example into the window, then click <strong>File </strong>&#x25B8;<strong> Save</strong>. Enter <code>hello.py</code> as the file name, and save it to your desktop. Then, you can click <strong>Run &#x203A; Run Module</strong> to run it.</p><p>An alternative to IDLE for Python is <strong>PyCharm</strong>, which is loved by many Python programmers. You can download the free PyCharm Community Edition from their <a href="https://www.jetbrains.com/pycharm/download/?ref=hackerculture.com">website</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.hackerculture.com/content/images/2023/01/image-4.png" class="kg-image" alt="A Gentle Introduction to Python Programming" loading="lazy" width="1600" height="930" srcset="https://www.hackerculture.com/content/images/size/w600/2023/01/image-4.png 600w, https://www.hackerculture.com/content/images/size/w1000/2023/01/image-4.png 1000w, https://www.hackerculture.com/content/images/2023/01/image-4.png 1600w" sizes="(min-width: 720px) 720px"><figcaption>Opening Pycharm shows a Tip of the Day message</figcaption></figure><p>PyCharm has a lot of extra features that make writing Python code really easy. You can use it to create new programs and to run saved programs directly from within PyCharm.</p><h3 id="run-your-python-program-from-the-terminal">Run your Python program from the Terminal</h3><p>We can also run our programs from the Terminal by taking these steps:</p><h3 id="for-windows-1">For Windows</h3><ol><li>Open the <strong>Command Prompt</strong> from the <strong>Start Menu</strong> or Windows search.</li><li>Enter <code>cd Desktop</code> in the command window to change the directory to the folder containing our program.</li><li>Type <code>python hello.py</code> to run the program.</li></ol><h3 id="for-mac-and-linux">For Mac and Linux</h3><ol><li>Open the <strong>Terminal</strong> app from Applications.</li><li>Enter &#xA0;<code>cd ~/Desktop</code> in your terminal to change the directory to the folder containing the program.</li><li>Type <code>python3 hello.py</code> to run it.</li></ol><p>With some luck, you should see the result of your Python program printed out!</p><h2 id="recap">Recap</h2><p>You just learned how to set up Python and how to start playing with it! Also: </p><ul><li>How to save your programs</li><li>How to create variables to name values</li><li>How to use parentheses to control the order of operations.</li></ul><p>Now would be a great time for a celebratory dance! &#x1F389;</p>]]></content:encoded></item><item><title><![CDATA[Running Multiple Processes in Python - A Multiprocessing Guide]]></title><description><![CDATA[Learn how to write Python programs that can run simultaneously on all processors available and also as background processes.]]></description><link>https://www.hackerculture.com/running-processes-python-multiprocessing-guide/</link><guid isPermaLink="false">6399036339dcbf003d6aa046</guid><category><![CDATA[Python]]></category><category><![CDATA[multiprocessing]]></category><category><![CDATA[concurrency]]></category><category><![CDATA[parallelism]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Mon, 26 Sep 2022 04:00:00 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/rishi-WiCvC9u7OpE-unsplash-1-.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/rishi-WiCvC9u7OpE-unsplash-1-.webp" alt="Running Multiple Processes in Python - A Multiprocessing Guide"><p>We can make Python run faster on multi-core CPUs, but one does not <em>simply</em> make use of multiple cores. We must design our programs carefully to leverage all available cores in a given machine effectively.</p><p>In this guide, you will learn the difference between regular (sequential) and parallel processing. You&apos;ll also learn about different techniques for running multiple processes simultaneously, effectively side-stepping the limitations of the Python interpreter.</p><p>By the end of the guide, you&apos;ll be able to write programs that run in parallel on all processors available and also as background processes.</p><h3 id="table-of-contents">Table of Contents</h3><ol><li><a href="#introduction">Introduction</a></li><li><a href="#cpu-bound-vs-io-bound-tasks">CPU-Bound vs. IO-Bound Tasks</a></li><li><a href="#race-conditions-and-pitfalls">Race Conditions and Pitfalls</a></li><li><a href="#processes">Processes</a></li><li><a href="#process-pool">Process Pool</a></li><li><a href="#processpoolexecutor">ProcessPoolExecutor</a></li><li><a href="#coordination-between-processes">Coordination Between Processes</a></li><li><a href="#sharing-state-between-processes">Sharing State Between Processes</a></li><li><a href="#when-to-use-processes">When to use Processes</a></li><li><a href="#why-not-use-threads">Why Not Use Threads</a></li><li><a href="#why-not-use-asyncio">Why Not Use Asyncio</a></li><li><a href="#recap-multiprocessing-best-practices">Recap: Python Multiprocessing Best Practices</a></li></ol><h2 id="introduction">Introduction</h2><p>Since the release of multi-core CPUs all the way back in 2001, we have seen processors grow faster than ever in their performance and capabilities. Modern smartphones and laptop computers today include as many as 8-core chips, and we can find high-end CPUs with up to 64 cores at affordable prices.</p><p>Most programs run in a single processor thread by default, so multi-core processors can run many of those programs simultaneously, using their different cores and threads. The OS coordinates with the CPU to run the programs on various cores as needed.</p><p>We can leverage multiple cores in Python and run parts of our programs simultaneously. But to be successful, we need to use the right strategy to run our tasks efficiently, depending on the task. This is known as multiprocessing and is the key to unlocking the full performance of our CPU.</p><p>Let&#x2019;s look at examples of particular tasks that may benefit from multiprocessing.</p><h3 id="cpu-bound-vs-io-bound-tasks">CPU-bound vs. IO-bound tasks</h3><p>Some multiprocessing applications are complex systems that must perform multiple tasks, often in the background while something more important takes place.</p><p>Databases use multiple programs to write new data while remaining able to answer read queries. Since database writes need to be saved, we need to wait for the write to disk to finish so we know the update was completed. This IO-bound task can only run as fast as a disk write, regardless of the kind of CPU.</p><p>Similarly, reading from a large filesystem also depends on disk read speeds. Just like reading from a network server depends on the speed of our network connection.</p><p>On the other hand, running computations in memory is a <em>CPU</em>-bound task, as the processor executes those instructions. For example: rendering graphics, compressing data, scientific computations, and video streaming all use processing to perform their job.</p><h3 id="race-conditions-and-pitfalls">Race conditions and pitfalls</h3><p>In a regular (aka sequential) program, the instructions are executed in order from start to finish. Every operation must finish before we can proceed with the next. The order of operations is straightforward, linear, and easy to reason about.</p><p>In a multiprocessing scenario, we can quickly run into trouble. Consider a program that saves its result in a file. If we executed this program in multiple cores concurrently, we can&#x2019;t tell which version of the program will be the last to write to this file.</p><p>This is called a <em>race condition</em>. Generally, if we have some shared resource that multiple processes need access to, we will need a way to coordinate between them, so they don&#x2019;t conflict. After all, the correctness of our program should not depend on accidents of timing.</p><p>One way of preventing race conditions is using <em>locks</em> (or <em>mutexes</em>), which grant exclusive access to the resource to a single process for a limited time, preventing others from accessing the resource until it&#x2019;s released. Something to keep in mind is that we must ensure the holder releases any acquired locks. Otherwise, other processes may wait indefinitely for access, and our program may never finish. This is called a <em>deadlock</em>.</p><h2 id="processes">Processes</h2><h3 id="what-is-a-process">What is a process</h3><p>A process is an instance of a program being executed by the CPU. A program is just a series of instructions typically stored on a disk file, and a process is the execution of that program that happens once the OS loads that program in memory and hands it off to the CPU.</p><p>Any given process can be executed concurrently by one or many threads if the CPU supports it.</p><p>In a multi-core processor, each core executes a single task at a time. However, the cores can switch between tasks, often while a process is waiting for input/output operation or when the OS decides it should switch tasks based on the task scheduler criteria, for example, after some time interval or depending on the number of processes waiting.</p><p>We can run multiple processes associated with the same program; for example, opening up several instances of the same program often results in more than one process being executed. However, we can only run as many processes simultaneously as the number of (logical) cores in our processor. The rest will queue up and will have to wait their turn.</p><p>Python can detect the number of logical cores in our system like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import os
# get the number of logical cpu cores
cores = os.cpu_count()
</code></pre><figcaption>Get the total number of processors available</figcaption></figure><h3 id="running-subprocesses">Running subprocesses</h3><p>So how can we run multiple processes associated with the same program? One example is running multiple instances of the same program, but we can also launch instances of different programs at will, using different methods.</p><p>One of these methods is called &quot;spawning.&quot;</p><p>Actually, <strong>spawning</strong> new processes is very straightforward and can be done by constructing a new <code>Process</code> instance and then calling <code>start()</code>.</p><figure class="kg-card kg-code-card"><pre><code class="language-Python"># create a process
process = multiprocessing.Process(target=task, args=(arg1, arg2))
# run the new process
process.start()
</code></pre><figcaption>Run a target function in a separate process</figcaption></figure><p>The <code>start() </code>function will return right away, and the OS will execute the target function in a separate process as soon as possible.</p><h3 id="naming-a-process">Naming a process</h3><p>We can give our processes a name for easier debugging and logging. By default, they will get a name assigned by the Python interpreter, but we can make their names more user-friendly!</p><p>Let&apos;s see this in practice:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from multiprocessing import current_process, Process

def task():
    process = current_process()
    print(&quot;subprocess is&quot;, process.name)

# entry point
if __name__ == &quot;__main__&quot;:
    process = current_process()
    print(&quot;parent is&quot;, process.name)

    # initialize Process with name
    other = multiprocessing.Process(target=task, name=&quot;database&quot;)
    other.start()

    other = Process(target=task)
    # assign a new name to the process
    other.name = &quot;other&quot;
    other.start()
</code></pre><figcaption>Give the process a debug-friendly name</figcaption></figure><h3 id="waiting-for-processes">Waiting for processes </h3><p>When we start a new process in Python, the <code>start()</code> function does not block the parent process. That means it creates a new process and returns immediately, regardless of how long the subprocess may take to run. We can explicitly wait for another process by calling the <code>join()</code> method.</p><p>This will block our main program and wait for the child process to terminate. Sometimes we don&apos;t want to wait for the process forever, though, and we may need to terminate it manually.</p><p>Here&apos;s an example:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">def task():
&#xA0;&#xA0;&#xA0;&#xA0;# block for a second
&#xA0;&#xA0;&#xA0;&#xA0;time.sleep(1)
&#xA0;&#xA0;&#xA0;&#xA0;print(&apos;in subprocess&apos;)

# entry point
if __name__ == &apos;__main__&apos;:
&#xA0;&#xA0;&#xA0;&#xA0;process = Process(target=task)
&#xA0;&#xA0;&#xA0;&#xA0;process.start()
&#xA0;&#xA0;&#xA0;&#xA0;# wait for the process to finish
&#xA0;&#xA0;&#xA0;&#xA0;print(&apos;Waiting for the process...&apos;)
&#xA0;&#xA0;&#xA0;&#xA0;process.join()
&#xA0;&#xA0;&#xA0;&#xA0;...
&#xA0;&#xA0;&#xA0;&#xA0;# or terminate the process
&#xA0;&#xA0;&#xA0;&#xA0;process.terminate()
</code></pre><figcaption>Use join() to block until the process finishes&#xA0;</figcaption></figure><h3 id="fork-vs-spawn">Fork vs. Spawn</h3><p>When starting a new process, the Python interpreter will hand off the process to the OS for execution. We should take into account how this handoff happens.</p><p>Spawning a new process means we will create a new Python interpreter instance from scratch to execute our new Python process. Then, Python will need to create copies of our process functions and references from the parent program to execute the new process concurrently. There is a performance hit during this initialization step.</p><p>On the other hand, <strong>forking</strong> is faster because it does not copy from the parent process; instead, the necessary resources are inherited and copied on write. This makes it trickier to coordinate resources between the parent and child process.</p><p>Forking is the default start method on Linux implementations, while macOS and Windows use spawn by default. However, because forking can lead to varying behavior, recent Python docs mention that the fork start method should be considered unsafe as it can lead to subprocesses crashing.</p><p>We can tell Python always to use spawn with this snippet:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">multiprocessing.set_start_method(&apos;spawn&apos;)
</code></pre><figcaption>Set the default start method to spawn</figcaption></figure><h3 id="security-concerns-when-starting-processes">Security concerns when starting processes</h3><p>When starting a subprocess on a typical Unix system, <code>fork()</code> is called, and the host process is copied. Modern Unix systems have a copy-on-write implementation of that syscall, meaning that the operation does not copy all the memory of the host process over. Forking is (almost) immediately followed by calling <code>execve()</code> in the child process &#x2014; which transforms the calling process into a new process.</p><p>While Python hides that away from us, we may need to call some OS functions directly and manage the creation of processes through the OS. For example, by using <code>system()</code> calls or invoking shell commands. The problem here is that <code>system()</code> calls expect arguments as strings and will use the shell to evaluate these arguments. This can be exploited by using malicious arguments to execute arbitrary commands or even get full shell access and hack the host system.</p><p>A secure way to interact with the OS is by using the subprocess module and setting the keyword <code>shell=False</code>.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">subprocess.run([&quot;ls&quot;, &quot;-l&quot;, &quot;/dev/null&quot;], shell=False, capture_output=True)
</code></pre><figcaption>Prevents shell injection with shell=False</figcaption></figure><h3 id="running-processes-in-the-background">Running processes in the background</h3><p>We may need to start a process to execute sporadic, periodic, or long-running tasks in the background. These are called &quot;daemon&quot; processes, like the demons or spirits from ancient Greek conjured to perform tasks for us in the background.</p><p>A process may be configured as a daemon or not, and most processes in concurrent programming, including the main parent process, are non-daemon processes (not background processes) by default.</p><p>Some use cases for daemon projects can be:</p><ul><li>Background logging to a file or database</li><li>Retrieving or querying data in the background</li><li>Storing data in a disk or database</li></ul><p>The main parent process will terminate once all non-daemon processes finish, even if daemon processes are still running. Therefore, the code it executes must be robust to arbitrary termination, such as the flushing and closing of external resources like streams and files that may not be closed correctly.</p><p>Daemon processes do not need to be waited for or manually terminated, as they will be closed when all non-daemon processes terminate. Even so, it is probably good practice to explicitly join all the processes we start.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from multiprocessing import Process, current_process

def task():
    print(&quot;daemon started&quot;)
    p = current_process()
    print(f&quot;daemon={p.daemon}&quot;)

if __name__ == &quot;__main__&quot;:
    daemon = Process(target=task, daemon=True)
    # once a process is started, this cannot be changed
    daemon.daemon = True
    daemon.start()
    daemon.join()
</code></pre><figcaption>Creating a daemon process</figcaption></figure><h2 id="process-pool">Process Pool</h2><p>Now, let&apos;s switch gears to learn how to leverage all the cores in our system, and we can do this in very few lines of code using multiprocessing Pools.</p><h4 id="starting-a-processing-pool">Starting a processing pool</h4><p>This is a great way to improve the parallelism of our program by just asking Python to distribute a task to all cores in the system. Let&#x2019;s see an example:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">from os import getpid
from multiprocessing import Pool, get_context

def squared(x):
    print(f&quot;started process {getpid()}&quot;)
    return x**2

if __name__ == &apos;__main__&apos;:
    with get_context(&quot;spawn&quot;).Pool() as pool:
        result = pool.map(squared, [1, 3, 5, 8, 13])
        print(result)
</code></pre><figcaption>Using a multiprocessing Pool to run a target function</figcaption></figure><p>As we can see, the square function runs in separate processes. Pay special attention to our <code>with</code> statement, where we ask the OS to use the spawn method. This can save some of the headaches discussed before in <strong>Fork vs Spawn</strong> and should be standard practice.</p><h4 id="spawn-from-pools">Spawn from pools</h4><p>Also, note that while <code>set_start_method(&apos;spawn&apos;)</code> changes things globally, it&#x2019;s better to use &#xA0;<code>get_context(&quot;spawn&quot;)</code> in our pools, avoiding any side effects.</p><h2 id="processpoolexecutor">ProcessPoolExecutor</h2><p>The ProcessPoolExecutor class is a convenient wrapper for managing multiprocessing pools. It also provides better resource utilization by allowing processes to share resources and tasks.</p><p>When using a <code>ProcessPoolExecutor</code> we have more control over how many processes run at once, when new processes are created, and how they are created.</p><figure class="kg-card kg-code-card"><pre><code class="language-python"># entry point of the program
if __name__ == &apos;__main__&apos;:
    # create a process pool with 4 workers
    executor = ProcessPoolExecutor(max_workers=4)
</code></pre><figcaption>A ProcessPoolExecutor of multiple processes&#xA0;</figcaption></figure><p>Note that if <code>max_workers</code> is not set, the pool will default to the number of cpu cores on the machine.</p><p>The <code>ProcessPoolExecutor</code> class defines three methods used to control our process pool: <strong>submit()</strong>, <strong>map()</strong>, and <strong>shutdown()</strong>.</p><ul><li><strong>submit()</strong>: Schedule a function to be executed and returns a Future object.</li><li><strong>map()</strong>: Apply a function to an iterable of elements and returns an iterator.</li><li><strong>shutdown()</strong>: Shut down the executor.</li></ul><h3 id="running-tasks-with-map">Running tasks with map()</h3><p>The <strong>map()</strong> function will return an iterator immediately. This iterable can be used to access the results from the target task function as they are available in the order that the tasks were submitted.</p><figure class="kg-card kg-code-card"><pre><code class="language-python"># perform all tasks in parallel
# returns an iterator and does not block
for result in executor.map(my_task, my_items):
    print(result)
</code></pre><figcaption>Use the map() method to execute a target function</figcaption></figure><h3 id="submitting-tasks-with-submit">Submitting tasks with submit()</h3><p>This method takes a function to call and all arguments to the function, then returns a Future object immediately.</p><p>The <strong>Future</strong> object is a promise to return the results from the task (if any) and provides a way to determine whether or not a specific task has been completed.</p><figure class="kg-card kg-code-card"><pre><code class="language-python"># submit a task with arguments and get a future object
future = executor.submit(my_task, arg1, arg2) # does not block
# get the result from a future
result = future.result() # blocks
</code></pre><figcaption>Submit a task with args to the pool executor</figcaption></figure><p>The <code>ProcessPoolExecutor</code> is started when the class is created and must be shut down explicitly by calling <strong>shutdown()</strong>, which will release any resources.</p><figure class="kg-card kg-code-card"><pre><code class="language-python"># shutdown the process pool
executor.shutdown() # blocks
executor.shutdown(wait=False) # doesn&apos;t block
# cancel all queued tasks
executor.shutdown(cancel_futures=True) # blocks
</code></pre><figcaption>Shutting down the executor</figcaption></figure><p>Note that if we don&apos;t close the process pool, it will be closed automatically when we exit the main thread. If we forget to close the pool and there are still tasks, the main process will only exit once all the queued tasks have been executed.</p><p>We can also shut down the <code>ProcessPoolExecutor</code> automatically, using a context manager for the pool. This is actually the preferred way:</p><figure class="kg-card kg-code-card"><pre><code class="language-python"># create a process pool
with ProcessPoolExecutor(max_workers=10) as executor:
    # submit tasks and get results
    results = executor.map(my_task, my_items)
    ...
# pool is shutdown at this point
</code></pre><figcaption>Automatically shut down the pool executor with context manager</figcaption></figure><h2 id="coordination-between-processes">Coordination Between Processes</h2><p>A <em>mutual exclusion</em> lock or mutex lock is used to protect critical sections of code from concurrent execution and to prevent a race condition.</p><p>A mutex lock can be used to ensure that only a <strong>single process</strong> executes a critical section of code at a time. All other processes trying to execute the same code must wait until the currently executing process is finished with the critical section and releases the lock.</p><p>We have a mutual exclusion (mutex) lock in the <code>multiprocessing.Lock</code> class.</p><p>Each process must attempt to acquire the lock at the beginning of the critical section. If the lock has not been obtained, then a process will acquire it, and other processes must wait until the process that acquired the lock releases it.</p><pre><code class="language-python"># create a lock
lock = multiprocessing.Lock()
# acquire the lock
lock.acquire()
...
# release the lock
lock.release()
</code></pre><p>Only one process can have the lock at any time. If a process does not release an acquired lock, it cannot be acquired again.</p><p>The process attempting to acquire the lock will block until the lock is acquired, such as if another process currently holds the lock and then releases it.</p><p>We can also use the lock with a context manager, allowing the lock to be released automatically once the critical section is completed.</p><pre><code class="language-python"># create a lock
lock = multiprocessing.Lock()

# acquire the lock
with lock:
    # critical section only the holder of the lock can execute
</code></pre><p>A limitation of a (non-reentrant) mutex lock is that if a process has acquired the lock, it cannot acquire it again. In fact, this situation will result in a deadlock as it will wait forever for the lock to be released so that it can be acquired, but it holds the lock and will not release it.</p><p>A <strong>reentrant lock</strong> will allow a process to acquire the same lock again if it has already been acquired.</p><p>There are many reasons why a process may need to acquire the same lock more than once. Imagine critical sections spread across different functions, each protected by the same lock. A process may call into one critical section from another critical section.</p><p>To create a reentrant lock, we use the <code>multiprocessing.RLock</code> class instead of a regular Lock.</p><h2 id="sharing-state-between-processes">Sharing State Between Processes</h2><p>When dealing with multiple processes, we typically need to share data and state between processes. We should wrap the shared data using the following classes:</p><ul><li><strong><code>multiprocessing.Value</code></strong>: manage a shared value.</li><li><strong><code>multiprocessing.Array</code></strong>: manage an array of shared values.</li></ul><p>For example, a shared ctype value can be defined in a parent process, then shared with multiple child processes. All child and parent processes can then safely read and modify the data within the shared value.</p><p>This can be useful in many cases, such as:</p><ul><li>A counter shared among multiple processes.</li><li>Returning data from a child process to a parent process.</li><li>Sharing results of computation among processes.</li></ul><p>Python also provides a process-safe <strong>queue</strong> in the <strong><code>multiprocessing.Queue</code></strong> class.</p><p>This shared job queue implementation allows queued items to be processed in parallel by multiple workers. This type of queue can store and transfer any <em>pickle-able*</em> object across process boundaries.</p><p>We can add items to a queue for later processing using the <code>put()</code> method and retrieve the items in subprocesses by calling its <code>get()</code> method.</p><p>We could also use a simple queue to send data back and forth between two processes, called a <strong>Pipe</strong>.</p><p>For example:</p><pre><code class="language-Python"># create a pipe
rec, send = multiprocessing.Pipe()
</code></pre><p>By default, the first connection (<code>rec</code>) can only be used to receive data and the second connection (<code>send</code>) can only be used to send data.</p><p>We can make the connection objects bidirectional by setting the <code>duplex</code> argument to the constructor to <code>True</code>.</p><pre><code class="language-python"># create a duplex pipe
conn1, conn2 = multiprocessing.Pipe(duplex=True)
</code></pre><p>In this case, we can use both connections to send and receive data.</p><p>Objects can be shared between processes using the Pipe.</p><p>We have a <strong><code>connection.send()</code></strong> to send objects from one process to another.</p><pre><code class="language-python"># send an object
conn2.send(&apos;Hello world&apos;)
</code></pre><p>We can then use the <strong><code>connection.recv()</code></strong> method to receive objects in one process sent by another.</p><pre><code class="language-python"># will block until an object is received
object = conn1.recv()
</code></pre><p>We can check the status of the pipe using the <strong><code>connection.poll()</code></strong> method. This will return a boolean as to whether there is data to be received and read from the pipe.</p><pre><code class="language-python"># returns True if there is data to read from the pipe.
if conn1.poll():
    ...
</code></pre><p>We can set a timeout with the &quot;<strong>timeout</strong>&quot; argument. If specified, the call will block until data is available. The function will return if no data is available before the timeout number of seconds has elapsed.</p><pre><code class="language-python"># waits for 5 seconds
if conn1.poll(timeout=5):
    ...
</code></pre><h2 id="when-to-use-processes">When to use Processes</h2><p>Python only executes a single thread at a time because the Global Interpreter Lock (<strong>GIL</strong>) requires each thread to acquire a lock on the interpreter before executing, preventing all other threads from executing simultaneously.</p><p>Although we may have tens, hundreds, or even thousands of concurrent threads in our Python application, only one thread may execute in parallel.</p><p>Process-based concurrency is not limited in the same way as thread-based concurrency.</p><p>Both threads and processes can execute concurrently (out of order), but only python processes can execute in parallel (simultaneously), not Python threads (with some caveats).</p><p>This means that if we want our Python code to run on all CPU cores and make the best use of our system hardware, we should use process-based concurrency.</p><p>If process-based concurrency offers true parallelism in Python, why not always use processes?</p><p>First, only one thread can run at a time within a Python process under most situations, except:</p><ol><li>When we are using a Python interpreter that does not use a GIL.</li><li>When we are performing IO-bound tasks that release the GIL.</li><li>When we are performing CPU-bound tasks that release the GIL.</li></ol><p>For example, when we are reading or writing from a file or socket, the GIL is released, allowing multiple threads to run in parallel.</p><p>Common examples include:</p><ul><li><strong>Hard disk drive</strong>: Reading, writing, appending, renaming, and deleting files, etc.</li><li><strong>Peripherals</strong>: mouse, keyboard, screen, printer, serial, camera, etc.</li><li>Internet: Downloading and uploading files, getting a webpage, querying RSS, etc.</li><li><strong>Database</strong>: Select, update, delete, etc. SQL queries.</li><li><strong>Email</strong>: Send mail, receive mail, query inbox, etc.</li></ul><p>Additionally, many CPU-bound tasks that are known to not rely on the state of the Python interpreter will release the GIL, such as C-code in third-party libraries called from Python.</p><p>For example:</p><ul><li>When performing compression operations, e.g. in <strong>zlib</strong>.</li><li>When calculating cryptographic hashes, e.g. in <strong>hashlib</strong>.</li><li>When performing encoding/decoding images and video, e.g, in OpenCV.</li><li>When performing matrix operations, e.g. in NumPy and SciPy.</li><li>And many more.</li></ul><p>Second, processes and process-based concurrency also have limitations compared to threads.</p><p>For example:</p><ol><li>We may have thousands of threads, but perhaps only tens of processes.</li><li>Threads are small and fast, but processes are large and slow to create and start.</li><li>Threads can share data quickly and directly, whereas processes must pickle and transmit data to each other.</li></ol><h2 id="why-not-use-threads">Why Not Use Threads</h2><p>Threads and processes are quite different, and choosing one over the other must be quite intentional.</p><p>A Python program is a process that has a main thread. You can create many additional threads in a Python process. You can also fork or spawn many Python processes, each of which will have one main thread and may spawn additional threads.</p><p>Threads are lightweight and can share memory (data and variables) within a process, whereas processes are heavyweight and require more overhead and impose more limits on sharing memory (data and variables).</p><p>Because of the GIL, only a single thread can execute at a time, making them a good candidate for workloads where we need to switch between tasks that spend a good amount of time waiting, like IO-bound tasks.</p><p>CPU-bound tasks won&apos;t benefit from multiple threads and will actually slow down because of the overhead of switching between multiple tasks without allowing them a fair share of CPU resources. </p><p>You can learn more about when to use threads in our guide:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.hackerculture.com/python-threads-multithreading-guide/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Threads in Python - The Multithreading Guide</div><div class="kg-bookmark-description">In this guide, you will learn about threads, their use cases, and how to use them to run multiple parts of your Python programs concurrently. When used correctly, they can make your code run faster and more efficiently with very little effort.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.hackerculture.com/favicon.ico" alt="Running Multiple Processes in Python - A Multiprocessing Guide"><span class="kg-bookmark-author">Hacker Culture</span><span class="kg-bookmark-publisher">Adrian Cruz</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.hackerculture.com/content/images/2023/01/photo-1619472351888-f844a0b33f5b_crop-entropy-cs-tinysrgb-fit-max-fm-jpg-ixid-MnwxMTc3M3wwfDF8c2VhcmNofDExfHx0aHJlYWRzfGVufDB8fHx8MTY3MjE3MjI0Mg-ixlib-rb-4.0--1-.webp" alt="Running Multiple Processes in Python - A Multiprocessing Guide"></div></a></figure><h2 id="why-not-use-asyncio">Why Not Use Asyncio</h2><p><a href="https://www.hackerculture.com/python-asyncio-guide-to-asynchronous-programming/">Asyncio</a> is a newer alternative to using a <strong>threading.Thread</strong>, but it&apos;s probably not a good replacement for the <code>multiprocessing.Process</code> class.</p><p>Asyncio is designed to support large numbers of IO operations, perhaps thousands to tens of thousands, all within a single Thread.</p><p>When using the <code>multiprocessing.Process</code> class, we&apos;re typically executing CPU-bound tasks, which is not something that benefits from the asyncio paradigm, and so it&apos;s not a good alternative for CPU-intensive workloads.</p><p>You can learn more about when to use asyncio in python in our guide:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.hackerculture.com/python-asyncio-guide-to-asynchronous-programming/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Python asyncio - A Guide to Asynchronous Programming</div><div class="kg-bookmark-description">In this guide, we&#x2019;ll introduce asynchronous programming in Python and review fundamental concepts like how to define, create and run asynchronous functions, coroutines, and some common use cases and best practices.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.hackerculture.com/favicon.ico" alt="Running Multiple Processes in Python - A Multiprocessing Guide"><span class="kg-bookmark-author">Hacker Culture</span><span class="kg-bookmark-publisher">Adrian Cruz</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.hackerculture.com/content/images/2023/01/olaf-ZT3mEdrBS7Y-unsplash.webp" alt="Running Multiple Processes in Python - A Multiprocessing Guide"></div></a></figure><h2 id="recap-python-multiprocessing-best-practices">Recap: Python Multiprocessing Best Practices</h2><p>Now that we know processes work and how to use them, let&#x2019;s review some best practices to consider when bringing multiprocessing into our Python programs.</p><p>Best practices to keep in mind when using processes in Python:</p><ol><li>Always Check for Main Module Before Starting Processes</li><li>Use Context Managers</li><li>Use Timeouts When Waiting</li><li>Use Locks to Prevent Race Conditions</li><li>Use Values, Pipes, and Queues for Shared State</li></ol><p>These best practices will help you avoid many concurrency pitfalls like race conditions and deadlocks.</p><p>By now, you should know how multiprocessing works in Python in great detail and how to best utilize processes in your programs.</p><h3 id="references">References</h3><ul><li><a href="https://docs.python.org/3/library/multiprocessing.html?ref=hackerculture.com">multiprocessing module</a></li><li><a href="https://superfastpython.com/multiprocessing-in-python/?ref=hackerculture.com">Python Multiprocessing: The Complete Guide</a></li><li><a href="https://pythonspeed.com/articles/python-multiprocessing/?ref=hackerculture.com">Why your multiprocessing Pool is stuck</a></li><li><a href="https://amzn.to/3uPRIQ0?ref=hackerculture.com">Effective Python: 90 Specific Ways to Write Better Python</a></li><li><a href="https://amzn.to/3HwbgAv?ref=hackerculture.com">Python in a Nutshell</a></li><li><a href="https://amzn.to/3FQi7Uo?ref=hackerculture.com">Python Cookbook</a></li></ul><p><strong>Did you find this guide helpful?</strong><br>If so, I&apos;d love to know. Please share what you learned in the comments below.</p><p><strong>How are you using Processes in your programs?</strong><br>I&#x2019;d love to hear about it, too. Please let me know in the comments.</p><p><strong>Do you have any questions?</strong><br>Leave your question in a comment below, and I will answer it with my best advice.</p>]]></content:encoded></item><item><title><![CDATA[Making Ubuntu Faster than Ever]]></title><description><![CDATA[Ubuntu is one of the most popular distros out there. It's pretty fast by default, but it can also slow down because of bloat and poor management. Today we are discussing some optimizations we can make to get back to fast ubuntu. Let's go!]]></description><link>https://www.hackerculture.com/making-ubuntu-fast/</link><guid isPermaLink="false">609eac23141eb7003b5e3115</guid><category><![CDATA[Ubuntu]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Fri, 04 Jun 2021 19:00:00 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/grahame-jenkins-I3c0mihe9ZE-unsplash-1-.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/grahame-jenkins-I3c0mihe9ZE-unsplash-1-.webp" alt="Making Ubuntu Faster than Ever"><p>Ubuntu is one of the most popular distros out there. It&apos;s pretty fast by default, but it can also slow down because of bloat and poor management. Today we are discussing some optimizations we can make to get back to fast ubuntu. Let&apos;s go!</p><p>The first step is to remove any bloatware that came preinstalled that could be updated or just unnecessary. </p><pre><code class="language-bash">sudo apt autoremove --purge</code></pre><h2 id="boot-optimizations">Boot optimizations</h2><p>Another thing holding you back is that your system starts unnecessary apps and services during startup. Ubuntu has the <em>Startup Applications</em> program that lets you remove any apps that may be slow to start. To show the complete list of startup apps and services, run this:</p><pre><code class="language-bash">sudo sed -i &apos;s/NoDisplay=true/NoDisplay=false/g&apos; /etc/xdg/autostart/*.desktop</code></pre><p>Now launch <em>Startup Applications</em> and disable any application by unchecking its box. Make sure to read the description so you disable only things you&apos;re sure you don&apos;t require!</p><figure class="kg-card kg-image-card"><img src="https://www.hackerculture.com/content/images/2021/05/Screenshot-from-2021-05-18-16-32-16.png" class="kg-image" alt="Making Ubuntu Faster than Ever" loading="lazy" width="800" height="703" srcset="https://www.hackerculture.com/content/images/size/w600/2021/05/Screenshot-from-2021-05-18-16-32-16.png 600w, https://www.hackerculture.com/content/images/2021/05/Screenshot-from-2021-05-18-16-32-16.png 800w" sizes="(min-width: 720px) 720px"></figure><h2 id="swap-only-when-you-need-to">Swap only when you need to</h2><p>Chances are you have plenty of RAM but you still feel like your machine is not fast enough. It could be that your OS is still using disk swap, instead of the full memory available. Ubuntu will actually use disk space when the RAM is <strong>40%</strong> full by default. That can be very low for machines with tons of RAM, so let&apos;s increase that.</p><pre><code class="language-bash">sudo sysctl vm.swappiness=10</code></pre><p>This will tell the kernel to only start using swap space when the RAM is 90% full. To make this change permanent, edit the <code>/etc/sysctl.conf</code> file in a text editor. Change the value of <code>vm.swappiness</code> or add it at the end of the file, like this:</p><pre><code>#
# /etc/sysctl.conf - Configuration file for setting system variables
# See /etc/sysctl.d/ for additional system variables.
# See sysctl.conf (5) for information.
#
...
#kernel.sysrq=438
vm.swappiness=10
</code></pre><h2 id="apt-fast">apt-fast</h2><p>The default package manager in ubuntu is <code>apt</code> &#x2013; Advanced Package Tool. But you can also use any other package manager you like. I will be sticking with <code>apt</code> because I like being able to copy/paste commands that I read on forums, blogs, etc. and I want it to <em>just work <sup>TM</sup></em>. However, one issue I have with <code>apt</code> is that it downloads packages sequentially, one after the other. So much for advanced, huh? We can do better by fetching packages in parallel, using multiple connections. That&apos;s exactly what <code>apt-fast</code> is for.</p><pre><code class="language-bash">sudo add-apt-repository ppa:apt-fast/stable
sudo apt update
sudo apt install apt-fast
echo &quot;alias apt=&apos;apt-fast&apos;&quot; &gt;&gt; ~/.bash_aliases
echo &quot;alias apt-get=&apos;apt-fast&apos;&quot; &gt;&gt; ~/.bash_aliases
</code></pre><h2 id="liquorix-patch">Liquorix patch</h2><p><a href="https://liquorix.net/?ref=hackerculture.com#features">Liquorix</a> is a custom kernel for Debian distros that uses a modern (<strong><strong>MuQSS</strong></strong>) process and disk scheduler for better performance and throughput. It also disables a lot of verbose logging, by turning off debug flags. Let&apos;s install that too:</p><pre><code class="language-bash">sudo add-apt-repository ppa:damentz/liquorix
sudo apt-get update
sudo apt install linux-image-liquorix-amd64 linux-headers-liquorix-amd64</code></pre><h2 id="preload-apps">Preload apps</h2><p>Preload is a program that runs in the background and analyzes the apps that you use to determine the ones you use frequently and fetches their binaries and dependencies in memory beforehand. This makes it so when you launch your apps, they start instantly from memory. The best thing about it is that you only have to install it once and it will make your apps start faster after that. You will be amazed!</p><pre><code class="language-bash">sudo apt install preload</code></pre><h2 id="tlp">TLP</h2><p>If your computer it&apos;s overheating, it will become slower to prevent damaging the core components of the CPU. This is called thermal throttling and it can impact the performance of your system. There are some programs that can help diagnose this problem, and even control how your CPU runs.</p><pre><code class="language-bash">sudo apt-get install tlp tlp-rdw
sudo tlp start
# TLP started in AC mode (auto).
sudo apt install indicator-cpufreq</code></pre><p>After your next restart, you will see an indicator next to the date/time (up on the status bar) from which you can select what speed your CPU should run at.</p><figure class="kg-card kg-image-card"><img src="https://www.hackerculture.com/content/images/2021/05/Screenshot-from-2021-05-18-16-25-53.png" class="kg-image" alt="Making Ubuntu Faster than Ever" loading="lazy" width="355" height="255"></figure><h2 id="alternative-distros">Alternative distros</h2><p>Of course, it would be nice to start out with a distro that enabled all these performance optimizations from the start. That means less tweaking and more fun. One such distro is <a href="https://www.linuxliteos.com/?ref=hackerculture.com">Linux Lite</a>, which is a modified version of ubuntu (using an Xfce desktop) with some nice tools for performance optimization, like Lite Tweaks and preload. This is a seriously snappy distro that can run on any machine with at least 1Gb of RAM.</p><p>And that&apos;s all I have. Don&apos;t forget to restart your system after tweaking the performance settings, and enjoy a faster Ubuntu!</p><hr><p><em>If you have other tips on how to improve Ubuntu performance, I&apos;d like to hear them! Please let us know in the comments below. </em></p>]]></content:encoded></item><item><title><![CDATA[Basic Python Data Types]]></title><description><![CDATA[In this one, we're taking a deep dive into the built-in types that are available in python and how to use them.]]></description><link>https://www.hackerculture.com/basic-python-data-types/</link><guid isPermaLink="false">609d967f141eb7003b5e2f24</guid><category><![CDATA[Beginner]]></category><category><![CDATA[Python]]></category><category><![CDATA[Fundamentals]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Fri, 28 May 2021 19:00:00 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/artur-tumasjan-05804iCnNcQ-unsplash.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/artur-tumasjan-05804iCnNcQ-unsplash.webp" alt="Basic Python Data Types"><p>Python is a <em>batteries-included</em> language. This means it ships with everything you need to get things working without installing and compiling external packages. That includes many data types and collections to help you manage the data your program needs. Today we&apos;re taking a deep dive into the built-in types that are available in python and how to use them.</p><h2 id="booleans">Booleans</h2><p>Boolean values are simple <code>True</code> or <code>False</code> values to represent logical truth values. These values can also be combined in boolean algebraic expressions to represent logical operations like <code>==</code>, &#xA0;<code>and</code>, <code>or</code>, and <code>not</code> . Here&apos;s an example of how we use booleans.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; is_true = True
&gt;&gt;&gt; is_false = False
&gt;&gt;&gt; is_true == is_true
True

&gt;&gt;&gt; is_true and is_false
False

&gt;&gt;&gt; is_true and not is_false
True

&gt;&gt;&gt; 1 == True and 0 == False
True</code></pre><figcaption>Use booleans to represent True and False values&#xA0;</figcaption></figure><h2 id="numeric">Numeric</h2><p>Numeric types are used to represent numbers or amounts. Python has a few types of numbers, the main ones being <code>int</code> and <code>float</code>.</p><p>Here&apos;s an example:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; one = 1  		 # an int value
&gt;&gt;&gt; one_float = 1.0  # a float value
&gt;&gt;&gt; total = 1 + 2    # an int value = 3
&gt;&gt;&gt; big = 1234567890123456789012345678901234567890  # also an int</code></pre><figcaption>Python numbers are represented as int or floats</figcaption></figure><p>Note in the last example that python has arbitrary integer precision, which means it can hold really large numbers!</p><h2 id="decimal">Decimal</h2><p>Even though we can represent floating-point numbers using float types, <a href="https://docs.python.org/3/tutorial/floatingpoint.html?ref=hackerculture.com">floats have some limitations</a> in how they&apos;re represented internally and can lead to precision issues. Check this out:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; .1 + .1 + .1
0.30000000000000004 # WTF
&gt;&gt;&gt; .1 + .1 + .1 == 0.3
False</code></pre><figcaption>Floating point arithmetic considered dangerous</figcaption></figure><p>Hmmm, so even though we&apos;re just adding <code>0.1</code> three times, somehow we end up with a value that&apos;s larger than <code>0.3</code>. It is only larger by a very small fraction, but still. If we were adding up transactions in a bank account, that could result in giving a user more money than their original amount. So to avoid precision issues, we should use decimals to guarantee correctness.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; from decimal import Decimal

&gt;&gt;&gt; dec = Decimal(&apos;0.1&apos;)
&gt;&gt;&gt; dec + dec + dec
Decimal(&apos;0.3&apos;)

&gt;&gt;&gt; dec + dec + dec == Decimal(&apos;0.3&apos;)
True</code></pre><figcaption>Decimals are a better alternative to floating point numbers</figcaption></figure><p>Decimal can be initialized from integers, strings, floats, or tuples. But if you initialize them from float values, you will run into the same issues as using floats, so just use strings!</p><h2 id="strings">Strings</h2><p>Strings are for representing text data. Like names, addresses, and so on. You can initialize a string by surrounding its value with quotes, either double, single, or even triple quotes, like so:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; a_string = &quot;Hello World!&quot;
&gt;&gt;&gt; escaped = &quot;I contain \&quot;double quotes\&quot;&quot;

&gt;&gt;&gt; long_string = &quot;&quot;&quot; This is a long string \
... with multiple lines &quot;&quot;&quot;

&gt;&gt;&gt; singles = &apos;Can also use single quotes&apos;</code></pre><figcaption>Add quotes around text to create strings</figcaption></figure><p>The de-facto standard is to use double quotes for string values and triple quotes for comments that span multiple lines.</p><p>Strings are a sequence that is indexed starting at zero. You can access any element in the string by index, so in our previous example, <code>a_string[0]</code> is the letter <code>H</code>, while <code>a_string[1]</code> is the letter <code>e</code> , and so on. Strings also have a lot of useful methods.</p><h2 id="tuples">Tuples</h2><p>Sometimes we have two pieces of data that are related to each other and should be stored together. For example, a first name and last name. Or a point represented by <code>x, y</code> or even more than two pieces, like a 3d point such as <code>x, y, z</code>. In python, we can represent that using a Tuple, which is a container type. To create tuples, simply wrap your values with parenthesis.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; point = (0, 3)
&gt;&gt;&gt; point3d = (0, 3, 12)
&gt;&gt;&gt; person = (&apos;Jon&apos;, &apos;Snow&apos;)
&gt;&gt;&gt; mixed = (&apos;Jon&apos;, 23, 1992)  # tuple with mixed types</code></pre><figcaption>A tuple can contain mixed data types and cannot be changed later</figcaption></figure><p>Those are all tuples, and they come in very handy. Something to note is that you cannot add more elements to a tuple after creating it. But you can access the elements in a tuple by index, just like strings. So in the example above, <code>point[0]</code> &#xA0;would evaluate to <code>0</code>, while <code>point[1]</code> gives <code>3</code>. Another cool trick is that you can get multiple elements out of a tuple by assigning multiple variables. This is also known as <em>unpacking</em> a tuple:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; x, y, z = point3d  # assigning multiple values
&gt;&gt;&gt; x
0

&gt;&gt;&gt; y
1

&gt;&gt;&gt; z
12</code></pre><figcaption>Unpacking a tuple or sequence</figcaption></figure><h2 id="lists">Lists</h2><p>Lists are similar to tuples in that they can hold any number of values, but it is mutable after created. So we can add more to it later. To initialize a list, we add square brackets around its values, like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; letters = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
&gt;&gt;&gt; letters
[&apos;a&apos;, &apos;b&apos;, &apos;c&apos;]

&gt;&gt;&gt; letters.append(&quot;x&quot;)  # add a single value
&gt;&gt;&gt; letters
[&apos;a&apos;, &apos;b&apos;, &apos;c&apos;, &apos;x&apos;]

&gt;&gt;&gt; letters.extend([&quot;y&quot;, &quot;z&quot;])  # add multiple values
&gt;&gt;&gt; letters
[&apos;a&apos;, &apos;b&apos;, &apos;c&apos;, &apos;x&apos;, &apos;y&apos;, &apos;z&apos;]

&gt;&gt;&gt; letters[0]
&apos;a&apos;
&gt;&gt;&gt; letters[-1]
&apos;z&apos;</code></pre><figcaption>Lists can be modified after created</figcaption></figure><p>Note how we can also get elements from the list by index. In the last example, we are accessing <code>letters[-1]</code>, which means the first value from the end. We can also remove elements from the list or merge values from two lists using <a href="https://docs.python.org/3/tutorial/datastructures.html?ref=hackerculture.com#more-on-lists">list methods.</a></p><h2 id="sets">Sets</h2><p>Another container type similar to the list is the <strong>set</strong>. The difference is that a list can contain duplicated values, while a set only contains unique values. Adding a value to a set that is already in the set does nothing.</p><p>Another crucial difference is that lists maintain the order of elements as they are added, while sets do not guarantee any particular order.</p><p>Sets are pretty useful when we only care about unique values.</p><p>For example:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; vowels = {&quot;a&quot;, &quot;e&quot;, &quot;i&quot;, &quot;o&quot;, &quot;u&quot;}  # initializes a set
&gt;&gt;&gt; vowels
{&apos;e&apos;, &apos;u&apos;, &apos;a&apos;, &apos;o&apos;, &apos;i&apos;}  # note the order changed

&gt;&gt;&gt; vowels.add(&apos;a&apos;)
{&apos;e&apos;, &apos;u&apos;, &apos;a&apos;, &apos;o&apos;, &apos;i&apos;}

&gt;&gt;&gt; vowels.remove(&apos;u&apos;)
&gt;&gt;&gt; vowels
{&apos;e&apos;, &apos;a&apos;, &apos;o&apos;, &apos;i&apos;}</code></pre><figcaption>A set of unique values</figcaption></figure><p>We can also perform set operations to join, merge, or even exclude elements from the set. <a href="https://docs.python.org/3/tutorial/datastructures.html?ref=hackerculture.com#sets">See some more examples here</a>.</p><h2 id="dictionaries">Dictionaries</h2><p>As the name implies, dictionaries or <code>dict</code> in python land, are useful for mappings or data which is identified by some key and has some value associated with that key. An example of this is a real-life dictionary, in which you have the word (key) and the definition as the value. A phone book (remember those?) is another kind of dictionary where the keys are the phone numbers and the values are the information associated with the owner of the phone number, such as name, address, etc.</p><p>Let&apos;s see some dict examples:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; MLB_team = {
...     &apos;Colorado&apos; : &apos;Rockies&apos;,
...     &apos;Boston&apos;   : &apos;Red Sox&apos;,
...     &apos;Minnesota&apos;: &apos;Twins&apos;,
...     &apos;Milwaukee&apos;: &apos;Brewers&apos;,
...     &apos;Seattle&apos;  : &apos;Mariners&apos;
... }

# alternatively, use the dict() constructor
&gt;&gt;&gt; MLB_team = dict(
...     Colorado=&apos;Rockies&apos;,
...     Boston=&apos;Red Sox&apos;,
...     Minnesota=&apos;Twins&apos;,
...     Milwaukee=&apos;Brewers&apos;,
...     Seattle=&apos;Mariners&apos;
... )

&gt;&gt;&gt; type(MLB_team)
&lt;class &apos;dict&apos;&gt;

&gt;&gt;&gt; MLB_team
{&apos;Colorado&apos;: &apos;Rockies&apos;, &apos;Boston&apos;: &apos;Red Sox&apos;, &apos;Minnesota&apos;: &apos;Twins&apos;,
&apos;Milwaukee&apos;: &apos;Brewers&apos;, &apos;Seattle&apos;: &apos;Mariners&apos;}</code></pre><figcaption>Dicts associate key-value pairs</figcaption></figure><p>Unlike lists and tuples, <code>dict</code> items are accessed by their <strong>key</strong> instead of by index.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; MLB_team[&apos;Minnesota&apos;]
&apos;Twins&apos;

&gt;&gt;&gt; MLB_team[&apos;Colorado&apos;]
&apos;Rockies&apos;

&gt;&gt;&gt; MLB_team[&apos;Toronto&apos;]
Traceback (most recent call last):
  File &quot;&lt;pyshell#19&gt;&quot;, line 1, in &lt;module&gt;
    MLB_team[&apos;Toronto&apos;]
KeyError: &apos;Toronto&apos;</code></pre><figcaption>Accessing a missing key results in a KeyError exception</figcaption></figure><p>If you try to access a key that is not in the dictionary, python throws a <code>KeyError</code>. You can also add and update keys by setting a new value for the key:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; MLB_team[&apos;Kansas City&apos;] = &apos;Royals&apos;
&gt;&gt;&gt; MLB_team
{&apos;Colorado&apos;: &apos;Rockies&apos;, &apos;Boston&apos;: &apos;Red Sox&apos;, &apos;Minnesota&apos;: &apos;Twins&apos;,
&apos;Milwaukee&apos;: &apos;Brewers&apos;, &apos;Seattle&apos;: &apos;Mariners&apos;, &apos;Kansas City&apos;: &apos;Royals&apos;}</code></pre><figcaption>Updating the value for a given key</figcaption></figure><p>Any given key can only appear in a dictionary once. If you try to set a duplicated key, it will just update the value associated with it. For more info on dictionaries, check out the <a href="https://docs.python.org/3/library/stdtypes.html?ref=hackerculture.com#mapping-types-dict">list of supported operations</a>.</p><h2 id="iterators">Iterators</h2><p>We have discussed a few different types of containers, like strings, lists, sets, and dictionaries. In python, we can iterate over the elements in a container using iterators.</p><p>The iterator is a special type that represents the position in a sequence and has only one operation, which is <code>next()</code>. If you&apos;ve worked with database cursors, iterators are a similar concept.</p><p>Here&apos;s an example:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; my_list = [1, 2, 3]
&gt;&gt;&gt; iterator = iter(my_list)

&gt;&gt;&gt; next(iterator)
1
&gt;&gt;&gt; next(iterator)
2
&gt;&gt;&gt; next(iterator)
3</code></pre><figcaption>Iterators produce values when next() is called</figcaption></figure><p>Note that now the iterator has returned the last element in the list. Watch what happens if we call <code>next()</code> again:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; next(iterator)
StopIteration</code></pre><figcaption>Raises StopIteration at the end</figcaption></figure><p>Python raises a <code>StopIteration</code> exception to signal the end of the sequence. You should handle this using a <code>try/except</code> block.</p><h2 id="immutables">Immutables</h2><p>Sometimes you want your data to be read-only. Meaning you want to prevent changing their values once it&apos;s initialized. This is where immutable types come in handy.</p><p>In Python, strings and tuples are immutable. Once you initialize them, you can&apos;t change them, you can only assign a new value to them.</p><p>Lists, sets, and dictionaries are mutable after the fact since you can add, remove and update their values. However, python also has a <code>frozenset</code> which behaves like an immutable set and supports the same operations as regular sets. </p><p>There is no built-in <code>frozendict</code> but <a href="https://www.python.org/dev/peps/pep-0603/?ref=hackerculture.com">we might get one soon</a>. If you do need an immutable dictionary, you can install the <a href="https://pypi.org/project/frozendict/?ref=hackerculture.com">frozendict package with pip</a>. &#xA0;</p><h2 id="functions">Functions</h2><p>Functions are a special data type that only runs when we ask it to. Functions are created using a function definition, and they only have one operation, which is to call it. They can receive data to operate on as arguments. A function can also return some data back to the caller.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; def add(num, other):
...     return num + other
...

&gt;&gt;&gt; add(1, 1)
2

&gt;&gt;&gt; add(0.1, 0.2)
0.30000000000000004

&gt;&gt;&gt; type(add)
&lt;class &apos;function&apos;&gt;</code></pre><figcaption>Defining a new function to add numbers</figcaption></figure><p>Here we defined a function called <code>add</code> which receives two parameters and returns the sum of the parameters. We then called the function with different values and got back a result, which is the sum. Finally, we use another built-in function to inspect the <strong>type</strong> of our function, which is (duh) a <code>function</code>. <a href="https://docs.python.org/3/library/functions.html?ref=hackerculture.com#built-in-functions">See more built-in functions here.</a></p><h2 id="classes">Classes</h2><p>Classes are how we make objects in python. An object is just an abstraction for a data type composed of multiple pieces of data. For example, consider a dog. We might know many things about a dog, like its name, kind, age, and weight. We can represent all these pieces of data about a dog using strings or tuples, but a better approach is to use an object to hold all the information about our dog:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">class Dog:
    def __init__(self, name, kind, age):
        self.name = name
        self.kind = kind
        self.age = age</code></pre><figcaption>Classes can represent any complex types</figcaption></figure><p>Here we have defined a class that contains information about our dog. Now we can represent multiple types of dogs by creating instances.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; leo = Dog(&quot;Leo&quot;, &quot;german shepherd&quot;, 1)
&gt;&gt;&gt; neeko = Dog(&quot;Neeko&quot;, &quot;pitbull&quot;, 2)</code></pre><figcaption>Creating class instances with arguments</figcaption></figure><p>This creates two new <code>Dog</code> instances &#x2014; one for a one-year-old German shepherd dog named <strong>Leo </strong>and one for a two-year-old pitbull named <strong>Neeko</strong>. We can also access and update dog attributes by name:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; leo.age = 1.5
&gt;&gt;&gt; leo.age
1.5</code></pre><figcaption>Updating the instance fields</figcaption></figure><p>Classes are special too, because we can add functionality by defining methods that we can then call in the instances. Methods are just a special type of function which is associated with a specific class.</p><figure class="kg-card kg-code-card"><pre><code class="language-python">class Dog:
    def __init__(self, name, kind, age):
        self.name = name
        self.kind = kind
        self.age = age
        
    def bark(self):
    	return &quot;Woof, woof!&quot;
        
    def sit(self):
    	return &quot;Okay I&apos;m sitting.&quot;</code></pre><figcaption>Classes can contain methods with functionality</figcaption></figure><p>Now we can call these methods on our instances:</p><figure class="kg-card kg-code-card"><pre><code class="language-python">&gt;&gt;&gt; leo.bark()
&apos;Woof, woof!&apos;
&gt;&gt;&gt; leo.sit()
&quot;Okay I&apos;m sitting.&quot;</code></pre><figcaption>Calling instance methods</figcaption></figure><h2 id="none">None</h2><p>Finally, the last type in our list is <code>None</code>. This is just a special python type to represent empty values. It supports no other operations, and functions that don&apos;t have a return statement will implicitly return <code>None</code> when they&apos;re done.</p>]]></content:encoded></item><item><title><![CDATA[A Guide to Productivity]]></title><description><![CDATA[How to take your productivity to the next level with a simple system based on making lists & other tips.]]></description><link>https://www.hackerculture.com/guide-to-productivity/</link><guid isPermaLink="false">609ad7c19c8406003b5772d6</guid><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Sun, 23 May 2021 19:00:00 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/sebastian-svenson-d2w-_1LJioQ-unsplash.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/sebastian-svenson-d2w-_1LJioQ-unsplash.webp" alt="A Guide to Productivity"><p>Productivity is been talked about endlessly. It seems every day there&#x2019;s a <em>new app</em> or a <em>new book</em> or a new something out there that will change your life and revolutionize your productivity to take it to the <em>next level.</em></p><p>But will it, <em>really</em>? Or will it give you yet another thing to do before you realize it&#x2019;s not for you? How much will you have to pay for that course or that coach or that subscription<em>?</em> How much will that money be worth when you realize the only system that works is one that works for <em>you</em>?</p><p>And really, the only person who can make it work is you, by creating a system designed for your specific needs. So let&#x2019;s get to work on creating that system.</p><p>We can get started with a simple technique called <em>making a list</em>.</p><p>All you need is a good list to make a plan. You can quote me on that.</p><h3 id="a-todo-list">A Todo List</h3><p>You need a <strong>To-do</strong> list to keep track of things you <em>must do</em>. Like commitments or assignments or anything that <em>needs</em> to be done. You could take this further and categorize your list by due dates (this week, next week, next month, etc.) Everything that <em>must</em> be done goes on this list.</p><h3 id="a-watch-list">A Watch List</h3><p>The <em>watch</em> list is everything you need to <em>remember</em> or <em>wait</em> for someone else to get back to you, or <em>follow up</em> on.</p><h3 id="a-later-list">A Later List</h3><p>Everything else goes into another list of things that you want to do when you have the time or wish you could do <em>sometime later</em>.</p><p>~</p><p>If something doesn&#x2019;t fit into any of these lists, then it must not be that important since you won&#x2019;t even want to do it later, so just ignore it.</p><p>Every day you will look at your <strong>To-do</strong> list and pick out 3 to 5 things that you want to do that day. Then you will try as much as possible to get those things done. If you do them, it will be a good day, and you will feel accomplished.</p><p>If you get into this habit, you will realize how many days went by before when you didn&#x2019;t get a single important thing done. But every time you do something, you get to cross it off your list and get that rush of endorphins, making you feel all proud and productive.</p><p>A cool thing about this list is that if you find yourself procrastinating on a particular to-do on your list, you can pick something else to do in the meantime!</p><p>As <a href="http://www.structuredprocrastination.com/?ref=hackerculture.com">John Perry</a>, a philosophy professor from Stanford would put it:</p><blockquote>The list of tasks one has in mind will be ordered by importance. Tasks that seem most urgent and important are on top. But there are also worthwhile tasks to perform lower down on the list. Doing these tasks becomes a way of not doing the things higher up on the list. With this sort of appropriate task structure, the procrastinator becomes a useful citizen. Indeed, the procrastinator can even acquire, as I have, a reputation for getting a lot done.</blockquote><h3 id="only-check-your-email-twice-a-day">Only check your email twice a day</h3><p>Doing email only twice a day will make you more productive the rest of the day. That&#x2019;s because you&#x2019;ll be distracted less often and will have more time to focus on the tasks at hand. Anyone who absolutely cannot wait until the end of the day will try to call you, text you, or reach you via smoke signals. Emails kill your flow and take away your ability to focus on something long enough to be productive.</p><p>You can take this further by categorizing your email with labels and filters, so all messages go into an <em>Action</em>, <em>Pending</em>, or <em>Review</em> folder. Everything else you should archive as soon as the conversation is done.</p><h3 id="turn-off-your-notifications">Turn off your notifications</h3><p>Same as with email, being constantly notified of your friend&apos;s latest adventures/meals is distracting and interrupts your flow. It&#x2019;s best just to turn off all the notifications and check your phone when you have time.</p><h3 id="don%E2%80%99t-answer-the-phone">Don&#x2019;t answer the phone</h3><p>Unless you&#x2019;re waiting for an important call, just keep your phone on silent and let all calls go to voice mail. If it&#x2019;s important, they will leave a message. Then when you have time, go through your voicemail and return the important calls.</p><p>Don&#x2019;t ever answer calls from random phone numbers. There are a lot of robo-callers, spam marketers, and even scammers out there. Don&#x2019;t fall for that.</p><p>I only ever answer calls from known numbers that I have in my contacts. <em>That&#x2019;s it.</em></p><h3 id="start-the-day-right">Start the day right</h3><p>No matter what you do or what time you wake up, just make sure you start the day right. That might be by having a nice breakfast or a light workout, or even some meditation. Whatever suits you, it&#x2019;s important to have a chance to peacefully collect your thoughts and prepare mentally and emotionally for the day ahead.</p><p>It will also allow you to review your <strong>To-do</strong> list and gather anything you need for your next task!</p><p>And that&#x2019;s it! Do you have a different productivity system already in place? Let me know in the comments below!</p><p>~</p><p>PS. Other people recommend <a href="https://hbr.org/tip/2020/07/make-a-to-dont-list?ref=hackerculture.com">making a <strong>To-Don&apos;t</strong> list</a> to identify busy work that isn&#x2019;t helping you meet your goals and help you focus on the work that is.</p>]]></content:encoded></item><item><title><![CDATA[Setting Up a Nodejs Developer Environment]]></title><description><![CDATA[In this one, I’ll show you how to set up your local machine for nodejs development so you can get started with node today. Let’s go!]]></description><link>https://www.hackerculture.com/setting-up-nodejs/</link><guid isPermaLink="false">609ac8d09c8406003b57723b</guid><category><![CDATA[Nodejs]]></category><dc:creator><![CDATA[Adrian Cruz]]></dc:creator><pubDate>Sat, 22 May 2021 19:00:00 GMT</pubDate><media:content url="https://www.hackerculture.com/content/images/2023/01/mohammad-rahmani-8qEB0fTe9Vw-unsplash.webp" medium="image"/><content:encoded><![CDATA[<img src="https://www.hackerculture.com/content/images/2023/01/mohammad-rahmani-8qEB0fTe9Vw-unsplash.webp" alt="Setting Up a Nodejs Developer Environment"><p>In a previous episode, we set up <a href="https://www.hackerculture.com/archive/python-setup/">Python for development like a Boss</a>.</p><p>In this one, I&#x2019;ll show you how to set up your local machine for <strong>nodejs</strong> development so you can get started with node today. Let&#x2019;s go!</p><h2 id="requirements">Requirements</h2><ul><li>Ubuntu 20.x / WSL</li><li>Internet connection</li></ul><h2 id="nvm-node-version-manager">nvm (node version manager)</h2><p>Similar to <strong>Pyenv</strong> for python, <strong>nvm</strong> is a program that allows us to manage multiple versions of <strong>node</strong> and makes switching between versions in different projects as seamless as it can be.</p><p>Let&apos;s install <strong>nvm</strong> following <a href="https://github.com/nvm-sh/nvm?ref=hackerculture.com#installing-and-updating">their instructions on Github</a>. Make sure you check Github to install the latest version if you&apos;re reading this from the future! Open up your terminal and run this command to install <strong>nvm</strong>.</p><pre><code class="language-bash">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash</code></pre><p>This will download <strong>nvm</strong>, install it locally, and add some lines to your <code>.bashrc</code> (or <code>.zshrc</code>) and will tell you something like &quot;restart your terminal to apply&quot;. So let&apos;s restart the terminal.</p><p>To verify that <strong>nvm</strong> is installed correctly, run this</p><pre><code class="language-bash">command -v nvm</code></pre><p>You should see that it prints <code>nvm</code> if it&apos;s working. If not, follow the <a href="https://github.com/nvm-sh/nvm?ref=hackerculture.com#troubleshooting-on-linux">troubleshooting guide</a>.</p><p>Now, we can install <strong>nodejs</strong> using <strong>nvm</strong>. Let&apos;s get the latest version by running:</p><pre><code class="language-bash">nvm install node # &quot;node&quot; is an alias for the latest version</code></pre><p>You should see some output ending like this</p><pre><code class="language-bash">...
Now using node v16.1.0 (npm v7.11.2)
Creating default alias: default -&gt; node (-&gt; v16.1.0)</code></pre><h2 id="installing-other-versions">Installing other versions</h2><p>The whole point of using <strong>nvm</strong> is that we can manage multiple versions of node. This is important because you might have some apps that require a specific version, while you can also test new features when a new version is released without messing with the previous versions required for other projects. In the previous step, we downloaded the latest version of node but it&apos;s recommended to stick with the <strong>Long Term Support</strong> versions for stability and long-term updates. This is what you want for apps you plan to maintain for a long time.</p><pre><code class="language-bash">nvm install --lts</code></pre><p>This installed <code>v14.17.0</code> for me today. Since this is the latest LTS version, I will also go ahead and tell my system to default to using this for every new shell.</p><pre><code class="language-bash"> nvm alias default 14
 # default -&gt; 14 (-&gt; v14.17.0)</code></pre><p>If I now run <code>nvm ls</code>, it will show me all my installed versions along with other available versions.</p><p>Now, my system is set up to use the <strong>LTS</strong> version that I just installed, but for newer projects where I&apos;d like to try more recent features added to <strong>nodejs</strong>, I want to use the latest version available. We can achieve this by creating a <code>.nvmrc</code> file in the project root directory (<em>or any parent directory</em>) to tell <strong>nvm</strong> which version it should use for this particular project.</p><pre><code class="language-bash">mkdir ~/src/nodeproj
cd ~/src/nodeproj
echo &quot;node&quot; &gt; .nvmrc # to default to the latest version</code></pre><p>Now if I just run <code>nvm use</code>, it&apos;ll look for that <code>.nvmrc</code> and switch to whatever it says in there that it should use.</p><p>If you&apos;d like, you can also set up <strong>nvm</strong> to automatically switch versions based on the local <code>.nvmrc</code> file (without having to enter <code>nvm use</code> manually). To do that, follow <a href="https://github.com/nvm-sh/nvm?ref=hackerculture.com#deeper-shell-integration">the official guide here</a>.</p><h2 id="bonus-yarn-package-manager">Bonus: yarn package manager</h2><p>We are now done with the node part. That was easy, right? But node wouldn&apos;t be what it is without its robust ecosystem of packages. The node maintainers support the official <a href="https://www.npmjs.com/?ref=hackerculture.com">npm</a> package manager which ships with every version of node. However, more recently <a href="https://techcrunch.com/2016/10/11/facebook-partners-with-google-others-to-launch-a-new-javascript-package-manager/?ref=hackerculture.com">Facebook introduced yarn</a> to optimize package resolution, offline support, and reproducible builds. And it&apos;s very fast! On average, about <strong>4x faster than npm</strong>.</p><p>To use <strong>yarn</strong>, you just need to run one command to install it via <strong>npm</strong> (oh the irony...)</p><pre><code class="language-bash">npm install -g yarn</code></pre><p>You can use <strong>yarn</strong> to install packages just like you would use npm. There are only minor differences in the syntax of the commands like <code>yarn add</code> as opposed to <code>npm install</code> and a few others. To help you get used to it, here&apos;s a handy <strong>yarn</strong> cheatsheet for ya!</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://devhints.io/yarn?ref=hackerculture.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Yarn cheatsheet</div><div class="kg-bookmark-description">The one-page guide to Yarn: usage, examples, links, snippets, and more.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://devhints.io/assets/favicon.png" alt="Setting Up a Nodejs Developer Environment"><span class="kg-bookmark-author">Devhints.io cheatsheets</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://assets.devhints.io/previews/yarn.jpg?t=20210506014007" alt="Setting Up a Nodejs Developer Environment"></div></a></figure><p>And that&apos;s all you need to start playing with <strong>nodejs</strong> today. Go write some code!</p><hr><p><em>Have other nodejs tips for getting started? Please let us know in the comments below!</em></p>]]></content:encoded></item></channel></rss>