Skip to main content

Harness the power of speLL with spellops to write clean and readable pipelines.

Project description

spellops

Developer Guide

Instead of writing this:

L(f_c(L(...).map(f_a).filter(f_b))).map(f_d)[0]

With spellops’s pipe operator, you can visually maintain the order of execution (f_a → filter(f_b) → f_c → f_d) by writing this:

L(...).map(f_a).filter(f_b).pipe(f_c).map(f_d)[0]

And with tee operator, you can inspect and document your pipeline like this:

(L(...)             .tee('input data')
    .map(f_a)       .tee('apply f_a to individual elements')
    .filter(f_b)    .tee('filter by f_b')
    .pipe(f_c)      .tee('apply f_c to the whole list')
    .map(f_d)[0]    .tee('finally apply f_d to the resulting elements and returning first value')
)

And once you’re done, you can keep all of this in your production code:

def process(data,verbose=False):
    s = dict(show=verbose)
    return (L(data)     .tee('input data', **s)
        .map(f_a)       .tee('apply f_a to individual elements', **s)
        .filter(f_b)    .tee('filter by f_b', **s)
        .pipe(f_c)      .tee('apply f_c to the whole list', **s)
        .map(f_d)       .tee('finally apply f_d to the resulting elements and returning first value', **s)
        .unwrap()       # Extract the element out of the list
    )

Here are some examples:

Import L and spellops

NOTE: order of import does not matters.

import spellops
from fastcore.foundation import L

Fluent string manipulaiton

L(['a', 'b', 'c']).map(str.upper).pipe('->'.join, wrap=False)
'A->B->C'

Or using convenience mehtod pipen that stands for pipe with wrap=False (no wrap):

L(['a', 'b', 'c']).map(str.upper).pipen('->'.join)
'A->B->C'

Count how many elements are present in both sublists

(L([[1,2,3,2],[2,4,1]])    .tee('input')
     .map(set)             .tee('to set')
     .starpipe(set.intersection)  .tee('common elements')
     .pipe(len)            .tee('count elements')
     .unwrap()
)
input
[[1, 2, 3, 2], [2, 4, 1]]
to set
[{1, 2, 3}, {1, 2, 4}]
common elements
[1, 2]
count elements
[2]

2

An alternative approach can be to pipe with wrap=False:

NOTE: In this case, you can’t use tee after the final pipe operator because it no longer returns an L instance.

(L([[1,2,3,2],[2,4,1]])    .tee('input')
     .map(set)             .tee('to set')
     .starpipe(set.intersection)  .tee('common elements')
     .pipe(len, wrap=False) # count elements
)
input
[[1, 2, 3, 2], [2, 4, 1]]
to set
[{1, 2, 3}, {1, 2, 4}]
common elements
[1, 2]

2

Solve an AoC task

SPOILER ALERT: the following is a speLL to solve AoC 2024 part B.

GOAL: For each number in the left list, we need to count how many times it appears in the right list, multiply these together, and sum all results. This creates a “similarity score”, counting occurrences and multiplying, that measures how frequently numbers from the left list appear in the right list.

sample = '''3   4
4   3
2   5
1   3
3   9
3   3
'''

# This function will be "mapped" to individual elements
def to_int_tuple(a,b): return (int(a),int(b))

# this function acts on the whole list
def count_instances(As,Bs): return [(o,len([t for t in Bs if t==o])) for  o in As]

from math import prod
(L(sample.splitlines())
                .map(str.split)         .tee('input data')
                .starmap(to_int_tuple)  .tee('int to tuples')
                .zip(cycled=True)       .tee('tuple of lists')
                .starpipe(count_instances)  .tee('apply count_instances to the "whole list"')
                .map(prod)              .tee('multiply tuple elements')
                .sum()
    )
input data
[['3', '4'], ['4', '3'], ['2', '5'], ['1', '3'], ['3', '9'], ['3', '3']]
int to tuples
[(3, 4), (4, 3), (2, 5), (1, 3), (3, 9), (3, 3)]
tuple of lists
[(3, 4, 2, 1, 3, 3), (4, 3, 5, 3, 9, 3)]
apply count_instances to the "whole list"
[(3, 3), (4, 1), (2, 0), (1, 0), (3, 3), (3, 3)]
multiply tuple elements
[9, 4, 0, 0, 9, 9]

31

Visually inspect images transformations:

import matplotlib.pyplot as plt
import numpy as np

def plot_images(x:L, msg):
    N = len(x)
    plt.figure(figsize=(4*N,4))
    for i,o in enumerate(x):
        plt.subplot(1,N,i+1)
        plt.imshow(o)
        plt.title(f'Image: {i}')
    plt.suptitle(msg)

(L([[[0,0,0],[1,1,1],[0,0,0]],[[1,0,0],[0,1,0],[0,0,1]]])  .tee('Input images',f=plot_images)
    .map(lambda x: np.rot90(x,k=1)) .tee('Rotate 90 degree',f=plot_images)
)
(#2) [array([[0, 1, 0],
       [0, 1, 0],
       [0, 1, 0]]),array([[0, 0, 1],
       [0, 1, 0],
       [1, 0, 0]])]

Install spellops in Development mode

# make sure spellops package is installed in development mode
$ pip install -e .

# make changes under nbs/ directory
# ...

# compile to have changes apply to spellops
$ nbdev_prepare

Usage

Installation

Install latest from the GitHub repository:

$ pip install git+https://github.com/artste/spellops.git

or from pypi

$ pip install spellops

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spellops-0.0.1.tar.gz (6.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

spellops-0.0.1-py3-none-any.whl (5.5 kB view details)

Uploaded Python 3

File details

Details for the file spellops-0.0.1.tar.gz.

File metadata

  • Download URL: spellops-0.0.1.tar.gz
  • Upload date:
  • Size: 6.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.11.11

File hashes

Hashes for spellops-0.0.1.tar.gz
Algorithm Hash digest
SHA256 d0064533e52751cb721c8a627614e3a0062b53137fda460795af800c2888c7f7
MD5 205e395833a61634b112a086027d1386
BLAKE2b-256 172d37796f763d1c472d1b31e0b0c93a8ba4fd68c9dec7949e214ec2892b5f5e

See more details on using hashes here.

File details

Details for the file spellops-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: spellops-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 5.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.11.11

File hashes

Hashes for spellops-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 174692d2c5ad9f27ac3ee812590e1d6794b216a10d9bab6007764e7bd8edf8a8
MD5 294806705f00b9a511eadd5d5fa3236c
BLAKE2b-256 ff49d530041d85bb4e408ed532c88a111715c8527eef53810a91883f0d204514

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page