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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.