Python - learn 80% of a new language in a week

Python - learn 80% of a new language in a week

Having spent my time with TS, Swift and Haskell I’m beyond excited to go back to an interpreted language. Python that is

Working with Lists - Wrangling data efficiently

Alright, fellow scholars, today we’re diving into the enigmatic world of lists, AKA arrays in ruby! Imagine you’re a mad scientist, and your lab bench is Python. Let’s get our hands dirty with some data wrangling that feels like brewing a potent potion. ๐Ÿงชโœจ

import string  # Summoning our trusty toolkit ๐Ÿงฐ

# Conjuring numbers 97 to 102 from the ether
result = list(range(97, 103)) \
    .filter(lambda x: x % 2 != 0) \  # Only the odd numbers are worthy ๐Ÿšซ๐Ÿ‘ซ
    .map(chr) \  # Transforming numbers to ASCII characters, like magic! โœจ
    .sorted(key=lambda x: 0 if x in "aeiouy" else 1) \  # Vowels lead the charge ๐ŸŽข
    .map(str.upper) \  # Amplifying their presence! ๐Ÿ”Š
    .slice(0, 2) \  # Selecting the elite duo
    .join('')  # Uniting them into a formidable alliance

Let’s break down this alchemical process:

  1. list(range(97, 103)): Imagine conjuring a series of numbers from 97 to 102 out of thin air. These aren’t just any numbers; they’re the secret codes for ASCII characters ‘a’ to ‘f’. ๐ŸŽฑ

  2. filter(lambda x: x % 2 != 0): We’re playing the role of gatekeeper, only allowing the odd ones to pass through. In our magical ASCII world, that means ‘a’, ‘c’, and ’e’. ๐Ÿƒ

  3. map(chr): With a flick of our wand, each number metamorphoses into its ASCII character counterpart. Thus, 97, 99, and 101 become ‘a’, ‘c’, and ’e’. ๐Ÿ”„

  4. sorted(key=lambda x: 0 if x in "aeiouy" else 1): Here, we’re organizing an exclusive soirรฉe, with vowels getting the VIP treatment. They effortlessly slide to the front, while the consonants might feel a bit left out. ๐ŸŽ‰

  5. map(str.upper): Post-sorting, our characters undergo a bold transformation, emerging in uppercase to make a statement. Loud and proud! ๐Ÿ“ข

  6. slice(0, 2): Now, it’s decision time. We select our two champions from the array of contenders. It’s like drafting the ultimate dream team. ๐Ÿ†

  7. join(''): Finally, our chosen heroes join forces, merging into a dynamic string duo that’s ready to take on the world. ๐Ÿ”—

In essence, our Python spell selects the most charismatic odd ASCII characters, gives vowels the red-carpet treatment, emboldens them, picks the top duo, and merges them into the formidable team of “AE”. ๐Ÿš€

What can I say - as a long time ruby user I love this code. NO buts or ifs.

JSON/XML/YAML Mapping - Converting between formats and objects

Now, let’s pivot to Python and tackle the original ruby challenge: working with XML and JSON like a scientist in their lab, mixing elements to create something new. We’ll start by crafting some XML, then morph it into a Python object, make a few tweaks, and finally, turn it into JSONโ€”all in one seamless experiment:

import xml.etree.ElementTree as ET
import json

# Step 1: Brewing some XML
xml_data = """
<friends>
  <friend>
    <name>Joey</name>
    <age>30</age>
  </friend>
  <friend>
    <name>Rachel</name>
    <age>29</age>
    <favoriteCoffee>Latte</favoriteCoffee>
  </friend>
</friends>
"""

# Step 2: Transforming XML to a Python Dictionary
root = ET.fromstring(xml_data)
friends_list = [
    {
        'name': friend.find('name').text,
        'age': int(friend.find('age').text),
        'favoriteCoffee': friend.find('favoriteCoffee').text if friend.find('favoriteCoffee') is not None else ''
    } for friend in root.findall('friend')
]

# Step 3: Time for some alchemy
for friend in friends_list:
    friend['age'] += 1  # A universal constant: everyone ages
    friend['favoriteCoffee'] = "Cappuccino" if not friend['favoriteCoffee'] else friend['favoriteCoffee']

# Step 4: Transmuting our Python Dict to JSON
json_output = json.dumps(friends_list, indent=4)

# Step 5: Unveiling our creation
print(json_output)

Here’s the breakdown of our scientific method:

  1. Brewing XML: We start by concocting a story of friends, Joey and Rachel, in XML. It’s like jotting down notes in your lab journal. ๐Ÿ“œ

  2. XML to Python Dictionary: Using Python’s ElementTree, a tool as essential as a beaker in a lab, we decode our XML into a structured Python dictionary. Think of it as distilling a complex compound into its base elements. ๐ŸŒ

  3. Conducting Some Edits: Like any good experiment, adjustments are key. We add a year to everyone’s age (time waits for no one!) and ensure all have a declared coffee preference. For Joey, who’s without, we opt for Cappuccino by default. โ˜•

  4. Python Dict to JSON: With the magic of json.dumps, our structured Python dictionary transforms into sleek, modern JSON. It’s the equivalent of turning your raw research data into a polished paper. ๐Ÿ’ซ

  5. The Grand Unveiling: Finally, we reveal our work, showcasing our friends in a vibrant new format, now with updated ages and coffee tastes. It’s presentation day, and your project is a hit! ๐Ÿ–จ๏ธ

And there you have itโ€”a journey from XML to JSON in Python, peppered with scientific curiosity and a dash of casual charm. ๐ŸŽฉโœจ

Dictionary Reversal - Flipping keys and values

Alrighty, Python wizards, let’s dive into the magical realm of Python dictionaries and perform some spellbinding swaps of keys and values! Just like concocting a potion with precise ingredients, we’ll handle the enchanting task of flipping dictionary entries, paying special attention to those tricky non-unique values. Here’s our formula for success:

def swap_keys_and_values(original_dict):
    # Preparing a new cauldron to brew our swapped concoction.
    swapped_dict = {}

    # Stirring through each ingredient (key-value pair) in the original dictionary.
    for key, value in original_dict.items():
        # If our potion already contains this ingredient as a key, we prepare to add more to it.
        if value in swapped_dict:
            if not isinstance(swapped_dict[value], list):
                swapped_dict[value] = [swapped_dict[value]]
            swapped_dict[value].append(key)
        else:
            # For unique ingredients, simply swap their roles.
            swapped_dict[value] = key

    return swapped_dict

# Testing our spell with a sample dictionary.
example_dict = {'a': 1, 'b': 2, 'c': 1}
print("Original:", example_dict)
swapped = swap_keys_and_values(example_dict)
print("Swapped:", swapped)

Here’s the spellbook breakdown:

  1. Conjuring the cauldron: We kick things off with an empty cauldron (a new dictionary) ready to hold our magical brew.

  2. Ingredient inversion: As we peruse through the original dictionary, we flip each ingredient (key-value pair) around. However, if our cauldron already contains this new ingredient as a key, we need to expand its container by transforming it into a potion bottle (an array) to welcome additional ingredients.

  3. Managing Multiples: Should we encounter an ingredient that’s already playing the role of a key in our new cauldron, we ensure it’s capable of holding several original keys by grouping them into a potion bottle (an array).

  4. Potion Presentation: We test our spell with an example dictionary, observing how it elegantly handles the scenario where ‘a’ and ‘c’ both have a liking for the number 1, showcasing our spell’s capability to deal with such love triangles.

Presto! We’ve brewed a Python function that elegantly flips dictionary keys and values, even when faced with the delicate situation of non-unique values. ๐Ÿ”„โœจ

For those moments when you’re after a quick flip and all values in your dictionary are as unique as unicorns, Python offers a straightforward approach with a dictionary comprehension:

example_dict = {'a': 1, 'b': 2, 'c': 3}
print("Original:", example_dict)  # Original: {'a': 1, 'b': 2, 'c': 3}
swapped = {value: key for key, value in example_dict.items()}
print("Swapped with comprehension:", swapped)  # Swapped with comprehension: {1: 'a', 2: 'b', 3: 'c'}

With this enchanting one-liner, we flip keys and values in a breeze. But tread carefully; this method assumes each value is a unique gem, as duplicates will cause the earlier ones to vanish into thin air, akin to overwriting in Ruby’s .invert.

So, if your dictionary’s values are as distinct as the stars, go ahead and use this quick enchantment. But if you sense the whisper of duplicates, you’ll want to approach it with the full spell we outlined first. ๐Ÿ”„๐Ÿš€

Handling IO - Getting dirty

Haskell left some marks on my psyche, so I will forever think about pure/unpure.

In the enchanted world of Python ๐Ÿ, we often venture into realms where the lines between pure and impure magic blurโ€”dealing with APIs ๐Ÿ“ก, whispering to databases ๐Ÿ’พ, echoing through the terminal ๐Ÿ–ฅ๏ธ, and sometimes leaving things to the whims of chance ๐ŸŽฒ. In the grand tradition of Haskell ๐Ÿง™โ€โ™‚๏ธ, actions that touch the world beyond our script’s scope are considered “dirty” due to their unpredictable side effects. But fear not, for this journey is about embracing the cleanliness of pure functions to craft more reliable, debuggable, and testable spellsโ€”err, code.

Here’s our quest:

  1. Gather Data from a CSV Scroll: Harness Python’s CSV module to read data.
  2. Sift Through the Data with a Spell: Apply logic to filter data.
  3. Conjure Data from an API for Each Entry: Simulate API invocations.
  4. Invoke a Moment of Chance: Add a random delay to each simulated API call.
  5. Inscribe the Results onto a New Scroll: Save the refined data to a new CSV file.
  6. Mark Our Progress: Display our advancement through the data.
import csv
import json
import requests
from time import sleep
from random import uniform

# Example CSV content (ensure this is saved as input.csv in your working directory):
#
# Name,Age,Email
# John Doe,28,john@example.com
# Jane Smith,22,jane@example.com
# Don Joe,33,donjoe@example.com

# Reads data from a CSV file, returning rows as a list of dictionaries.
def read_csv_data(filename):
    with open(filename, newline='') as csvfile:
        return list(csv.DictReader(csvfile))

# Filters rows based on a given criterion.
def filter_data(rows, condition):
    return [row for row in rows if condition(row)]

# Fetches user data from an API for a given ID.
def fetch_user_data(id):
    try:
        response = requests.get(f"https://jsonplaceholder.typicode.com/users/{id}")
        sleep(round(uniform(0, 0.1), 3))  # Adding a sprinkle of randomness
        return response.json() if response.ok else {"error": f"Failed to fetch data for ID {id}"}
    except requests.RequestException:
        return {"error": f"Failed to fetch data for ID {id}"}

# Writes processed data to a CSV file.
def write_to_csv(filename, headers, data):
    with open(filename, 'w', newline='') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(headers)
        writer.writerows(data)

# The main enchantment tying everything together.
def process_csv(input_file, output_file):
    data = read_csv_data(input_file)
    filtered_data = filter_data(data, lambda row: int(row['Age']) > 25)
    
    processed_data = []
    for index, row in enumerate(filtered_data, start=1):
        user_data = json.dumps(fetch_user_data(index))
        print(f"Progress: {index}/{len(filtered_data)} rows bewitched.")
        processed_data.append(list(row.values()) + [user_data])
    
    write_to_csv(output_file, list(data[0].keys()) + ['APIResponse'], processed_data)
    print(f"All data transmuted and saved to {output_file}.")

# Incantation example
input_file = 'input.csv'
output_file = 'output_processed.csv'
process_csv(input_file, output_file)
Basic Project Setup - Hello World with options

To craft a Python program that greets the world from the command line, morphing its voice between a whisper and a shout based on our whims (and arguments!), we’ll split our charm into three scrolls: one main scroll (hello_world.py), one for the shout (upper_case.py), and one for the whisper (lower_case.py). This ensemble will let us greet the world in the default tone, all caps for emphasis, or in a gentle lowercase, depending on the secret word passed through the command line.

Scroll 1: upper_case.py This scroll is imbued with the power to amplify our greeting.

# upper_case.py
def upper_case():
    print("HELLO, WORLD!")

Scroll 2: lower_case.py This scroll calms our voice to a gentle whisper.

# lower_case.py
def lower_case():
    print("hello, world!")

Scroll 3: hello_world.py The main scroll, deciding the tone of our greeting based on the secret word whispered to it through the command line.

# hello_world.py
from upper_case import upper_case
from lower_case import lower_case
import sys

if len(sys.argv) > 1:
    command = sys.argv[1]
    if command == "upper":
        upper_case()
    elif command == "lower":
        lower_case()
    else:
        print("Hello, World!")
else:
    print("Hello, World!")

Summoning the Program from the Terminal To beckon the program into action, use the terminal. Navigate to the sanctuary holding your scrolls, and you can call forth the program in one of three manners:

  1. Default Incantation (echoes “Hello, World!”):

    python hello_world.py
    
  2. Shout (booms “HELLO, WORLD!”):

    python hello_world.py upper
    
  3. Whisper (murmurs “hello, world!”):

    python hello_world.py lower
    

This ensemble of scrolls demonstrates a basic yet powerful way to split your Python magic across multiple files, allowing you to command the tone of your program with ease and flexibility, much like a wizard deciding the strength of their spells based on the situation at hand.

AWS S3 Interaction - File operations in the cloud

Let’s conjure up a Python script that juggles AWS S3 like a magician, performing feats like uploading a file, shifting it between stages, refining its storage class, retrieving it, dicing it into manageable slices, and then compressing each slice with a spell of compression. For this mystical journey, we’ll employ boto3 for the S3 enchantments and gzip for the compression charm. Ensure these libraries are in your spellbook before we begin:

pip install boto3
pip install gzip

Here’s our magical blueprint:

  1. Craft a CSV out of thin air: Imagine creating a digital parchment filled with data.
  2. Elevate it to an S3 bucket: Picture uploading a treasured tome to a celestial library.
  3. Relocate the tome to another repository: Like deciding it belongs in a more prestigious section of the library.
  4. Enhance its storage essence: Envision moving your tome from a wooden shelf to a vault guarded by dragons for its protection.
  5. Summon the tome back to your hands: Because sometimes, you just need to hold your knowledge close.
  6. Segment the tome into scrolls: Like cutting a map into riddles to be solved piece by piece.
  7. Compress each scroll: Shrinking the scrolls as if by a potion to make them easier to carry on your journey.

The Enchantment Itself

import boto3
import csv
import gzip
import os

# Preparing our S3 mage
s3_client = boto3.client('s3', region_name='your-own-special-realm')

# The celestial library locations and the tome's title
source_bucket = 'your-ancient-library'
destination_bucket = 'your-modern-archive'
file_name = 'arcane_knowledge.csv'
object_key = f"mystic-scripts/{file_name}"

# 1. Inscribing the CSV parchment
csv_content = '\n'.join([','.join(['Name', 'Age']), ','.join(['Alice', '30']), ','.join(['Bob', '25'])])

# 2. Uploading the tome
s3_client.put_object(Bucket=source_bucket, Key=object_key, Body=csv_content)

# 3. Changing the tome's repository
s3_client.copy_object(
    CopySource={'Bucket': source_bucket, 'Key': object_key},
    Bucket=destination_bucket,
    Key=object_key
)

# 4. Bestowing the tome with a storage enchantment
s3_client.copy_object(
    CopySource={'Bucket': destination_bucket, 'Key': object_key},
    Bucket=destination_bucket,
    Key=object_key,
    StorageClass='GLACIER'
)

# 5. Retrieving the tome
with open(file_name, 'wb') as file:
    s3_client.download_fileobj(destination_bucket, object_key, file)

# 6 & 7. The segmentation and compression ritual
with open(file_name, 'rb') as file:
    with gzip.open(f"{file_name}.gz", 'wb') as gz:
        gz.writelines(file)

os.remove(file_name)  # Vanishing the original tome to keep only the compressed scrolls

print("The ritual is complete! Your tome has traversed many realms.")
OS Communication - Integrating with the host system

Let’s whip up a Python script that transforms your terminal into a command-line ninja ๐Ÿฅท, silently darting through your system to perform reconnaissanceโ€”like listing directory contents, assessing disk space, and identifying those CPU-hungry processes. Python’s subprocess module will be our stealthy ally, granting us the power to execute system commands and harness their outputs without breaking a sweat.

Here’s how to arm your ninja:

import subprocess

# Ninja technique to execute a command and capture its essence
def execute_command(cmd):
    try:
        # Splitting the command for subprocess
        process = subprocess.run(cmd.split(), check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        print(f"Executing: {' '.join(cmd)}")
        print("Output:\n" + process.stdout)
    except subprocess.CalledProcessError as e:
        print(f"Error executing {' '.join(cmd)}:\n" + e.stderr)

# Deploying the ninja on various quests

# Reconnaissance on directory contents
execute_command('ls -l')
# Sample Output:
# Executing: ls -l
# Output:
# total 32
# drwxr-xr-x  5 user user 4096 Jul  7 10:00 app
# -rw-r--r--  1 user user  569 Jul  7 09:57 Gemfile

# Surveying the disk usage
execute_command('df -h')
# Sample Output:
# Executing: df -h
# Output:
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/sda1        50G   15G   33G  30% /

# Tracking down CPU-hungry adversaries
execute_command('ps aux --sort=-%cpu | head -5')
# Sample Output:
# Executing: ps aux --sort=-%cpu | head -5
# Output:
# USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
# root      1588  7.5  0.1 247856 11944 ?        Ssl  09:14   0:01 /usr/lib/packagekit/packagekitd
# user     11768  6.5  2.5 1164032 205908 ?      Sl   09:15   0:03 /usr/bin/gnome-shell

These quests are but a glimpse into the capabilities of our Python ninja, as the true outcome of these commands will vary based on the secrets and current affairs of your system.

Remember, wielding such power requires caution. Before sending your ninja on a mission, ensure the commands are safe and won’t compromise your system’s integrity or reveal its secrets. Proceed with wisdom, and happy ninja-ing! ๐ŸŽ‰

Multiline Strings - Preserving formatting

Python also has a spell for crafting multiline strings, preserving the sanctity of your whitespace and indentation, much like inscribing runes with precision. We turn to Python’s triple quotes for this purpose, with f-strings allowing us to weave in variables dynamically, holding the essence of our formatting sacred:

# Preparing our variables, setting the stage
guest = "Alice"
party_location = "Wonderland"
time = "6 PM"
activity = "unbirthday party"

# Inscribing our detailed, multiline message
message = f"""
Hey {guest}! ๐ŸŽ‰

Guess what? You're invited to a magical party in {party_location}! ๐Ÿ„
When? At {time}, sharp. Don't be fashionably late!

We're throwing an {activity}, and it wouldn't be the same without you.

See you in {party_location}!
"""

print(message)

Here, the f before the opening """ activates Python’s magic, allowing us to embed expressions such as variables directly within the string. This method ensures our tale’s formattingโ€”its spaces, indents, and line breaksโ€”remains untouched, just as we’ve laid it out in our script, ensuring our message retains its intended charm and structure. ๐ŸŒˆโœจ

Python’s approach, much like Ruby’s, offers a seamless way to maintain the elegance of our output, making it perfect for those moments when every space and indent contributes to the story we wish to tell.

Code Reuse with Classes - Structured project organization

Translating this Ruby tutorial to Python, let’s create a Python project that mirrors the structure and functionality, utilizing class inheritance to generate dynamic and colorful outputs in the terminal. We’ll embrace Python’s class inheritance and make use of ANSI color codes for the terminal output to bring some vibrancy to our program.

Project Structure:

python_project/
|-- base/
|   `-- person.py
|-- people/
|   |-- friend.py
|   `-- family_member.py
`-- main.py

Step 1: The Base Class

Define the base class Person with a say_your_name method. This class will be housed in the base directory.

base/person.py:

class Person:
    def say_your_name(self):
        return "I'm just a basic Person."

Step 2: Inheriting Classes

Create classes that inherit from Person, each overriding the say_your_name method and placed in the people directory.

people/friend.py:

from base.person import Person

class Friend(Person):
    def say_your_name(self):
        return "I'm a Friend!"

people/family_member.py:

from base.person import Person

class FamilyMember(Person):
    def say_your_name(self):
        return "I'm a Family Member!"

Step 3: Dynamic Invocation and Colorful Output

Implement a script (main.py) to dynamically invoke the say_your_name method from a specified class and change the output color based on a command-line argument.

main.py:

from base.person import Person
from people.friend import Friend
from people.family_member import FamilyMember
import sys

# ANSI color codes
colors = {
    "red": "\033[31m",
    "green": "\033[32m",
    "default": "\033[0m"
}

# Determine class and color from command-line arguments
class_name = sys.argv[1] if len(sys.argv) > 1 else "Person"
color = sys.argv[2] if len(sys.argv) > 2 else "default"

# Mapping of possible classes
classes = {
    "Person": Person,
    "Friend": Friend,
    "FamilyMember": FamilyMember
}

# Dynamically select and instantiate the class
person = classes.get(class_name, None)()
output = person.say_your_name() if person else "Class not found."

# Print the name with the selected color
print(f"{colors.get(color, colors['default'])}{output}{colors['default']}")

Running the Program

To see the dynamic and colorful output in action, use the command line to navigate to the root of your Python project directory and run:

python main.py Friend green
python main.py FamilyMember red

This Python project setup retains the modularity and expandability of the Ruby version, allowing for easy addition of new “people” classes or colors by extending the people directory or updating the colors dictionary in main.py. This design keeps the project organized and scalable, facilitating easy management and enhancements.

Error Handling - Graceful data transformation inspired by monads

Implementing a Monad-like approach in Python for error handling can significantly streamline managing complex data transformations, ensuring code remains both clean and resilient. While Python doesn’t inherently support Monads in the way functional languages do, we can still craft a structure that mimics their behavior, especially the Maybe monad for gracefully handling uncertain situations.

Step 1: Craft the Monad
Let’s get our hands dirty by building a Maybe monad from scratch. It’ll have two states: Some when fortune smiles upon us with a value, and None for those times when fate leaves us empty-handed or we encounter an error. This construct will help us elegantly navigate through potential pitfalls without tripping over NoneType errors or exceptions.

class Maybe:
    def __init__(self, value=None):
        self.value = value

    @classmethod
    def some(cls, value):
        return cls(value)

    @classmethod
    def none(cls):
        return cls()

    def map(self, func):
        if self.value is None:
            return self
        try:
            return Maybe(func(self.value))
        except:
            return Maybe.none()

Step 2: Deploy the Monad for Data Transformation
Now, let’s put our Maybe monad to the test in a practical scenario: transforming user data. Our mission is to fetch user information, isolate the age, and then add a year. Each step in this journey could potentially falter, but with Maybe at our side, we’ll maintain grace under pressure.

# Mock functions for this data transformation journey

def fetch_user_data(user_id):
    # This is where things could go south
    if user_id is None or user_id <= 0:
        return Maybe.none()

    # Assuming success, we retrieve some user data
    return Maybe.some({"name": "Alice", "age": 30})

def extract_age(user_data):
    age = user_data.get('age')
    if age is None:
        return Maybe.none()
    
    return Maybe.some(age)

def increment_age(age):
    return Maybe.some(age + 1)

# Kicking off the data transformation process
def process_user_data(user_id):
    return fetch_user_data(user_id).map(lambda data: extract_age(data)).map(lambda age_maybe: age_maybe.map(lambda age: increment_age(age)))

Experimentation

# Example run

# **Case of smooth sailing:**
# Valid user ID and data includes age.
result = process_user_data(1)
if result.value is None:
    print("A snag was hit during the data transformation.")
else:
    print(f"Transformed data: {result.value.value}")  # Expected: Transformed data: 31

# **Case of hitting a bump:**
# 1. Invalid user ID (`None` or negative)
# 2. Missing age in the user data leading to extraction hiccup.
result = process_user_data(-1)  # Simulates fetch failure due to invalid ID.
if result.value is None:
    print("A snag was hit during the data transformation.")  # This will be printed.
else:
    print(f"Transformed data: {result.value.value}")

The Strategy Unveiled:

  • We initiate our quest by attempting to fetch user data. Should the user ID present itself as dubious (None or <= 0), we swiftly retreat with a Maybe.none().
  • Successfully retrieved data moves us to the next challenge: extracting the age. Failure to locate such a piece of information nudges us towards Maybe.none() once more.
  • With the age securely in hand, we bestow upon it an additional year through increment_age, a gesture always wrapped in Maybe.some().

Through chaining these maneuvers, we craft a sequence that’s both elegant and safeguarded against disruptions. Should any step falter, we gracefully bow out, ensuring our program remains robust and clear of unforeseen pitfalls. And there you have it, Pythonists, a monad-inspired guide to sophisticated error handling and data transformation. ๐Ÿš€

Multithreading and Async - Maximizing CPU usage

Let’s crank up the magic ๐ŸŽฉโœจ in our Python script by going multithreaded, akin to firing off a series of texts and eagerly awaiting all the replies in real-time. ๐Ÿš€ Here’s the wizardry required to transform our code into a parallel processing extravaganza:

import requests
import threading
import json

# Targets for our charm ๐ŸŽฏ
urls = [
    'https://jsonplaceholder.typicode.com/posts/1',
    'https://jsonplaceholder.typicode.com/posts/2',
    'https://jsonplaceholder.typicode.com/posts/3'
]

# Spell to fetch data from the ether ๐ŸŒ
def fetch_data(url):
    try:
        response = requests.get(url)
        data = response.json()
        # Sprinkle some emoji magic on the output ๐ŸŒŸ
        print(f"Fetched from {url}: {data['title']} ๐Ÿ“ฌ")
    except Exception as e:
        print(f"Oops! Failed to fetch {url}: {e} ๐Ÿšจ")

# Conjuring threads for each URL to initiate the fetch ๐Ÿงต
threads = [threading.Thread(target=fetch_data, args=(url,)) for url in urls]

# Release the threads! ๐ŸŽ‰
for thread in threads:
    thread.start()

# Ensuring all our mystical threads complete their quests ๐ŸŽ‰
for thread in threads:
    thread.join()

print("All done! Data fetched in parallel, like a wizard ๐Ÿ˜Ž")

Unraveling the Enchantment:

  • Targets of Our Spell ๐ŸŽฏ: Our eyes (or requests) are set on three distinct posts from jsonplaceholder.
  • Fetching the Data ๐ŸŒ: The fetch_data incantation reaches across the digital expanse to conjure up some JSON. Should it encounter a beastly error, it signals an alert with a ๐Ÿšจ.
  • Conjuring Threads ๐Ÿงต: For each URL, we summon a new thread into being. Imagine dispatching your familiars to gather ingredients from different corners of the realm, all at once.
  • Emoji-laden Revelations ๐Ÿ“ฌ: As each familiar returns with its bounty, it announces its success alongside a cheerful mailbox emoji, symbolizing the joy of receiving mystical messages.
  • The Grand Finale ๐ŸŽ‰: We wait at the crossroads for all our threads to return from their voyages before triumphantly proclaiming our mastery of parallel fetches with a ๐Ÿ˜Ž emoji.

Invoke this script within your Python sanctum to witness the terminal come alive with asynchronous wonder. It’s a splendid showcase of Python’s capability to handle multiple quests at once, all while keeping the proceedings enchantingly clear and lively.

Testing - be an adult, test your code

Let’s craft a Python program that embodies charm and utility in equal measure. We’ll conjure up a Greeter class dedicated to the art of salutation, then don our investigative caps ๐Ÿ•ต๏ธโ€โ™‚๏ธ to ensure our Greeter is performing its duties with the expected grace and precision.

The Python Program

Behold, our gracious Greeter class, residing in a script named greeter.py:

class Greeter:
    def greet(self, name):
        return f"Hello, {name}!"

The Test

Python brings its own set of tools for the task, with the unittest module at our disposal, requiring no additional incantations for installation. Hereโ€™s a spell to test our Greeter, enscribed in greeter_test.py:

import unittest
from greeter import Greeter

class GreeterTest(unittest.TestCase):
    def test_greet(self):
        greeter = Greeter()
        self.assertEqual(greeter.greet("Alice"), "Hello, Alice!", "Greeter should warmly greet Alice")
        self.assertEqual(greeter.greet("Bob"), "Hello, Bob!", "Greeter should cordially greet Bob")

if __name__ == '__main__':
    unittest.main()

In this incantation, we ensure our Greeter maintains its social graces by accurately addressing Alice and Bob. The assertEqual invocation is our method of verifying that upon meeting Alice, our Greeter indeed says, โ€œHello, Alice!โ€.

Conducting the Tests

To see if our Greeter ascends to the occasion ๐ŸŒˆ, embark upon the terminal, journey to the directory harboring greeter.py and greeter_test.py, and utter:

python greeter_test.py

Should harmony prevail, you shall be greeted with a message of success, indicating your Greeter has acquitted itself admirably. ๐ŸŽ‰ Should discord arise, fear not, for unittest will guide you to the source of the misstep, allowing you to coach your Greeter towards perfection.

Thus, we have not only a Python program of genteel demeanor but also the means to verify its conduct. Testing, while seemingly a chore, is indeed a stalwart ally, ensuring our creations behave as envisioned. Embark joyfully on your coding voyage! ๐Ÿš€

Wrap up

Transitioning from Ruby to Python and other languages like JavaScript (JS), TypeScript (TS), Elixir, Swift, and Haskell, you’re equipped with a solid foundation. Here are five key takeaways for your journey ahead:

  1. Universal Concepts: The principles of Object-Oriented Programming (OOP), functional programming hints, and testing strategies you’ve learned are applicable across many programming languages, including Python.
  2. Readability Focus: Pythonโ€™s syntax prioritizes readability, much like Ruby’s, making it easier to write clean and understandable code.
  3. Versatility: The versatility you appreciated in Ruby extends into Python, JS, TS, and beyond, each language offering unique features for different applications, from web development to data science.
  4. Testing Importance: Just as testing in Ruby ensured reliability, Python and other languages also emphasize testing for robust code development.

Keep exploring, and enjoy the journey across different programming landscapes!

Related Posts

My favourite tech discussions - or how to lose time and piss people off

My favourite tech discussions - or how to lose time and piss people off

Meet My Personal Favorite Tech Archetypes ๐Ÿš€ Swollen Mark - SAD Developer ๐Ÿค” Strategic Ambiguity Development (SAD) Swollen Mark embodies the Strategic Ambiguity Development style.

Read More
The Myth of the Non-Technical Engineering Manager

The Myth of the Non-Technical Engineering Manager

Does engineering manager need to be “technical”?

Read More
Deploying a Rails App to Kubernetes ๐Ÿšข

Deploying a Rails App to Kubernetes ๐Ÿšข

Deploying a Rails App to Kubernetes ๐Ÿšข Hey there!

Read More