Skip to main content

Functional Extensions

for Python

https://pypi.org/project/funext/

This repository aims to extend the Python programming language by adding some useful (functional) extensions to objects, functions and data structures. Some of those extensions include:

  • being able to chain pure (and mutating) functions by calling them directly on objects. Most of those functions are known from the built-in functions.

  • applying functions to objects and chaining them through a pipe-function

  • composing functions

Usage

from functional_extensions import l_, s_, d_, t_, f_

regular_list = [1, 2, 3, 4]
extended_list = l_(1, 2, 3, 4)

# all extended classes have a simple initializer function. Containers can either
# take individual values as *args, or a regular collection
extended_list = l_([1, 2, 3, 4]) # or
extended_list = l_(1, 2, 3, 4)

extended_set = s_(1, 2, 3, 4) # or
extended_set = s_(set(1, 2, 3, 4))

extended_dict = d_({'key': 'value'})

extended_tuple = t_(1, 2, 3, 4) # or
extended_tuple = t_((1, 2, 3, 4))

def add(a, b): return a + b
extended_function = f_(add)

Available functions

Object

pipe_(self, function, *args, **kwargs)

Applies the object self to a function function with the arguments from *args and **kwarngs and returns the result.


copy_(self) 
deepcopy_(self)

Copies self, either shallowly or deeply, and returns the result.


type_(self)

Returns type(self).

Sequences and Containers

Iterable

to_list_(self)
to_set_(self)
to_tuple_(self)

Converts the iterable self into the desired object.


map_(self, function, *args, **kwargs)

Applies every element of self to function and returns an iterable of the new elements. Passes *args and **kwargs to the map-function.

Returns an object of the same type as that of self.


for_each_(self, apply, *args, **kwargs)

For every element in self, function is called with said element, as well as *args and **kwargs. The list is then returned.


min_(self)
max_(self)
sum_(self)
all_(self)
any_(self)

Returns min(self), max(self), sum(self), all(self) and any(self) respectively.


sort_(self, key=None, reverse=False)

Sorts all elements from the iterable self in a new list and returns it. Calls the sorted-function under the hood with key and reverse.


enumerate_(self, start=0)

Returns an enumerate object from the iterable self.


zip_(self, *iterables)

Iterates over self and all iterables in *iterables, producing tuples with an item from each one.


filter_(self, condition)
filterfalse_(self, condition)

filter_ returns a new instance of this iterable with only the elements that condition returns true.

filterfalse_returns a new instance of this iterable with only the elements that condition returns false.

Reversible

reverse_(self)

Returns a new instance of the reversed iterable self.

Sized

len_(self)

Returns len(self)

MutableSequence

map_inplace_(self, apply, *args, **kwargs)

Applies every element of self to function and overwrites this element with the new value.

List

sort_inplace_(self, key=None, reverse=False)

Sorts the list in-place and returns the sorted list

Functions

compose_(self, function)

Composes a function


and_(self, other_function)
or_(self, other_function)
not_(self)

Composes two predicates.


partial_(self, *args, **kwargs)

Partially applys *args, and **kwargs to self and returns the partial function.


curry_(self, n=EMPTY)

Curries self.


complement_(self)

Cunstructs a negation of self.


all_fn_(self, *fs)
any_fn_(self, *fs)
none_fn_(self, *fs)
one_fn_(self, *fs)

Construct a predicate returning True when all, any, none or exactly one of self and fs return True.

Examples

Most of those functions should not need additional examples, as they are a mere re-phrasing of some basic concepts and functions of the Python programming language and funcy.

If you need an example anyway, you should consider looking at the test classes, which cover every function.

The why

An excerpt of the "zen of python":

Beautiful is better than ugly

A simple exercise. Initialize a list with the numbers [4, 1, 2, 3], take the negative square of these numbers, sort the list and print every element individually.

Which one of the following code pieces is more beautiful?

input = [4, 1, 2, 3]
squared = [-(x ** 2) for x in input]
squared.sort()
for element in squared:
  print(x)
def negative_square(x): return - (x ** 2)

l_(4, 1, 2, 3) \
    .map_inplace_(negative_square) \
    .sort_() \
    .for_each_(print) 

There are 4 things happening. In the top example, all things are expressed via a slightly different syntax. Initialization with a list literal, mapping by a list comprehension, calling the sort-function on the list and then printing all elements

In the bottom example, every "thing" that is happening, is expressed in a coherent way - by calling a function.

Requirement Corresponding Function
Initialize a list with the numbers [4, 1, 2, 3] l_(4, 1, 2, 3)
Take the negative square .map_inplace_(negative_square) 1
Sort the list .sort_()
Print every element individually .for_each_(print)

1 we could also use a lambda-function in-place to avoid having to declare an own function for this case. However, the code might become slightly less expressive. Which brings us to the next "principle"

Explicit is better than implicit

This principle means, that there shouldn't be anything unexpected happening under the hood, which is often the case in high-level codebases. Metaprogramming, making use of inheritance or just not choosing proper variable names can make the code hard to read and understand, making it more easy to make mistakes.

These extensions might make things more complex at first glance, but the naming is chosen quite carefully. If a function name corresponds to a name of a built-in function, this functions will not do more or less than exactly that function.

All other functions are designed to be pure, and not cause anything to happen outside of their little scope.

All function that are not pure, are explicitly named like that, for example map_inplace_

All functions that have similar names than built-in functions, have an explicit "_"-postfix to denote that they do things differently, for example sort_, which adheres to the rule that functions are pure, unlike the sort()- function from the builtin list-class.

Simple is better than complex

and

Readability counts

One might argue, that any code which doesn't directly solves a certain use case is, by definition, not simple. It might be correct to define complexity like that. But you can also define complexity by how much time it takes you to read and understand code. This library tries to help reduce complexity of the code itself, on a less abstract layer than the use-case-specific solutions are written in. By that, hopefully those solutions also become less complex.

There should be one-- and preferably only one --obvious way to do it.

Yes, in an attempt to fulfill other principles, this principle is arguably broken. Now there is one more way to write a for-loop or call a map-function. At least I try to keep this rule fulfilled within this library. There should be one obvious way to do one thing by using functional extensions.

Metadata

Release files for funext 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for funext 0.2.0
File Size Uploaded Provenance Actions
funext-0.2.0.tar.gz 12.8 kB Without provenance Details for funext-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for funext 0.2.0
File Interpreter ABI Platform Actions
funext-0.2.0-py3-none-any.whl Python 3 none any Details for funext-0.2.0-py3-none-any.whl

Total release size: 22.3 kB

Release files / funext-0.2.0.tar.gz

Download URL funext-0.2.0.tar.gz
Size 12.8 kB
Tags Source
SHA-256 checksum
How to use checksums
c0b8a3e83426e8c87c6c4cf1e6bcc6e894c421825c5dea0e22b16d77e67ab07c
BLAKE2b-256 checksum
How to use checksums
22252f07cd2f39399ad13485585f9856570edb34e9447dcd069f0dcd0e8c8c00
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.7.1 importlib_metadata/4.9.0 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.5

Release files / funext-0.2.0-py3-none-any.whl

Download URL funext-0.2.0-py3-none-any.whl
Size 9.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5585c5cf3d748ed4ccf78c72a0821bd7db733723ea800789c301e39eb4d72e7e
BLAKE2b-256 checksum
How to use checksums
c6c33b7f0bdbd2622f4badf29717abbebb975fcd73563dfae9aa96011f2bd19b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.7.1 importlib_metadata/4.9.0 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.5

Security for funext 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page