File: README.rst

package info (click to toggle)
python-first 2.0.2-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 124 kB
  • sloc: python: 90; sh: 12; makefile: 7
file content (133 lines) | stat: -rw-r--r-- 4,490 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
first: The function you always missed in Python
===============================================

.. image:: https://travis-ci.org/hynek/first.svg?branch=master
   :target: https://travis-ci.org/hynek/first
   :alt: CI Status

``first`` is an MIT-licensed Python package with a simple function that returns the first true value from an iterable, or ``None`` if there is none.
If you need more power, you can also supply a ``key`` function that is used to judge the truth value of the element or a ``default`` value if ``None`` doesn’t fit your use case.

N.B. I’m using the term “true” consistently with Python docs for ``any()`` and ``all()`` — it means that the value evaluates to true like: ``True``, ``1``, ``"foo"``, or ``[None]``.
But **not**: ``None``, ``False``, ``[]``, or ``0``.
In JavaScript, they call this “truthy”.


Examples
========

A simple example to get started:

.. code-block:: pycon

   >>> from first import first
   >>> first([0, None, False, [], (), 42])
   42

However, it’s especially useful for dealing with regular expressions in ``if/elif/else`` branches:

.. code-block:: python

   import re

   from first import first


   re1 = re.compile('b(.*)')
   re2 = re.compile('a(.*)')

   m = first(regexp.match('abc') for regexp in [re1, re2])
   if not m:
      print('no match!')
   elif m.re is re1:
      print('re1', m.group(1))
   elif m.re is re2:
      print('re2', m.group(1))

The optional ``key`` function gives you even *more* selection power.
If you want to return the first even number from a list, just do the following:

.. code-block:: pycon

   >>> from first import first
   >>> first([1, 1, 3, 4, 5], key=lambda x: x % 2 == 0)
   4

``default`` on the other hand allows you to specify a value that is returned if none of the elements is true:

.. code-block:: pycon

   >>> from first import first
   >>> first([0, None, False, [], ()], default=42)
   42


Usage
=====

The package consists of one module consisting of one function:

.. code-block:: python

   from first import first

   first(iterable, default=None, key=None)

This function returns the first element of ``iterable`` that is true if ``key`` is ``None``.
If there is no true element, the value of ``default`` is returned, which is ``None`` by default.

If a callable is supplied in ``key``, the result of ``key(element)`` is used to judge the truth value of the element, but the element itself is returned.

``first`` has no dependencies and should work with any Python available.


Alternatives
============

``first`` brings nothing to the table that wasn’t possible before.
However the existing solutions aren’t very idiomatic for such a common and simple problem.

The following constructs are equivalent to ``first(seq)`` and work since Python 2.6:

.. code-block:: python

   next(itertools.ifilter(None, seq), None)
   next(itertools.ifilter(bool, seq), None)
   next((x for x in seq if x), None)

None of them is as pretty as I’d like them to be.
The ``re`` example from above would look like the following:

.. code-block:: python

   next(itertools.ifilter(None, (regexp.match('abc') for regexp in [re1, re2])), None)
   next((regexp.match('abc') for regexp in [re1, re2] if regexp.match('abc')), None)
   next((match for match in itertools.imap(
       operator.methodcaller('match', 'abc'), [re1, re2]) if match), None)

Note that in the second case you have to call ``regexp.match()`` *twice*.
The third example "fixes" that problem but also summons Cthulhu.

For comparison, one more time the ``first``-version:

.. code-block:: python

   first(regexp.match('abc') for regexp in [re1, re2])

Idiomatic, clear and readable. Pythonic. :)

----

As of version 0.6.5 from 2015, the excellent `boltons package <https://boltons.readthedocs.io/>`_ contains a ``first``-like function as part of its `iterutils module <https://boltons.readthedocs.io/en/latest/iterutils.html#boltons.iterutils.first>`_.


Background
==========

The idea for ``first`` goes back to a discussion I had with `Łukasz Langa`_ about how the ``re`` example above is painful in Python.
We figured such a function is missing Python, however it’s rather unlikely we’d get it in and even if, it wouldn’t get in before 3.4 anyway, which is years away as of yours truly is writing this.

So I decided to release it as a package for now.  If it proves popular enough, it may even make it into Python’s stdlib in the end.


.. _`Łukasz Langa`: https://github.com/ambv