Welcome to Wonderful Wrappers’s documentation!

Contents:

Wonderful Wrappers: unobtrusive wrappers improving Python builtins and more

http://travis-ci.org/Tygs/ww.svg?branch=master http://coveralls.io/repos/github/Tygs/ww/badge.svg?branch=master Documentation Status Version published on the Python Package Index
  • Compatibility: CPython 2.7+/3.3+ and the last stable versions of pypy2.
  • Platform: Agnostic. But only tested on GNU/Linux for now.
  • Version: 0.2
  • Documentation.

Install with:

pip install ww

Ever wish you could...

lazily slice generators?

>>> from ww import g
>>> gen = g(x * x for x in range(100))
>>> gen
<IterableWrapper generator>
>>> for element in g(x * x for x in range(100))[3: 6]:
...     print(element)
...
9
16
25

add dictionaries?

>>> from ww import d
>>> dic = d({'a': 1})
>>> dic
{'a': 1}
>>>  dic + {'b': 2}
{'b': 2, 'a': 1}

have a len attribute on lists?

>>> from ww import l
>>> lst = l([1, 2, 3])
>>> lst
[1, 2, 3]
>>> lst.len
3

join() from tuple?

>>> from ww import t
>>> tpl = t((1, 2, 3))
>>> tpl
(1, 2, 3)
>>> tpl.join(',')  # oh, it also autocasts to string. And its configurable.
u'1,2,3'

replace() multiple caracters at once in a string?

>>> from ww import s
>>> string = s('fizz buzz')
>>> string  # strings try very hard to be consistent on Python 2 and 3
u'fizz buzz'
>>> string.replace(('i', 'u'), 'o')  # the original signature is ok too
u'fozz bozz'

And there are many, many, more goodies.

WARNING

The software is currently in early stage. Only s() and g() are considered well documented and tested, and even them deserve some more love. You’ll also meet some empty files for future ideas.

We choose to make an early release under the pressing request of colleagues eager to try it but it’s not the final product. Quality is on the way.

Also, we WILL break the API until we reach 1.0, from which we’ll switch to semver and secure the API.

Development

You can offer PR with your contributions to ww. They should include unit tests, docstrings, type definitions and a new entry in the documentation. And follow the style conventions:

Get the full repository:

And move inside the ww directory.

Install ww and the dependancies for dev:

python setup.py develop
pip install -r dev-requirements.txt

Deactivate dev mode:

python setup.py develop –uninstall

Running unit tests on your current Python:

python setup.py test

Run tests coverage with your current Python:

# cmd only coverage
py.test --cov ww tests
# dump an HTML report in htmlcov dir
py.test  --cov-report html --cov ww tests

We have many test environements to build the doc, validate the code against various checkers and linters or run unit tests on several Python interpreters.

You can list them all with:

tox -l

E.G:

$ tox -l
flake8
py35
py34
py33
py27
pypy2
doc
coverage
mypy
bandit

You can run them individually with:

tox -e env_name

E.G:

tox -e doc # builds the documentation

All envs with a name starting with “py” requires that you have the matching Python interpreter installed on your system to be ran.

E.G: py33 requires you to have CPython 3.3 installed on your machine, and pypy2
supposes you have PyPy2 on your machine.

The mypy, bandit and doc env require you to have Python3.5 installed.

Running all the tests in all envs can be done with:

tox

Before you do a PR, it’s better if you can do this, since it will run the the most tests. But remember if you don’t have the matching interpreters they will be skipped.

In any case, running the checkers and linters is strongly advised, as any PR failing them will be rejected.

Versioning scheme

Versioning follow SemVer, althoug we won’t commit to stability before version 1.0.

Release with X.Y.Z will be of 2 kinds:

  • if Y is odd, the release will add features.
  • if Y is even or Z > 0, the release will be dedicated to bug fixing, documentation, API improvment and performances.

E.G.:

  • 0.2.1: 1 > 0 so no new features.
  • 1.4.1: 4 is even, so no new features.
  • 2.1.0: 1 is odd, you may see new feature in this release.

Installation

Dependencies

Wonderful Wrappers (WW) obviously need a working Python installation, since it is designed to work with it. Any Python version installed at the time of reading should be compatible: Cpython from 2.7 to 3.5, Pypy 2 and 3…

In order to work nicely with both major Python versions, some compatibility external packages are needed, and will be automatically installed with the standard install procedure. For your information, those are the required external modules:

  • future
  • six

Additionnally, some other external packages are useful for WW, but not required: you can exploit most of the features of WW without them. Don’t worry, you’ll get clear error messages if you try to use a feature that need an optional dependency.

  • chardet
  • formatizer

Now for the fun part. Installing WW is as easy as this, typed in a console:

pip install ww

On a POSIX system (like GNU/Linux or UNIX), you may want to make this installation available only for the current user (and avoid system-wide install).

pip install --user ww

This will install the lightest set of features for WW, pulling the minimum dependencies. That would be useful for inclusion in a production-ready project, but you may prefer the full set of features while you play around with it (obviously, the –user option is still valid).

pip install ww[all]

There is more: you may want to accept some additional dependencies in your project, but not that one, that comes with the full set of WW. You can specify the exact features you want to be available with your WW installation. For example, this will install only the chardet dependency, after the required ones.

pip install ww[chardet]

The full list of options is here:

  • chardet (for automatic charset detections in strings conversions)
  • formatizer (for F-strings)

Alternatively, you can install WW from source. This enables to install the latest, fresh-baked, version from Github instead of PyPI. To do so, you first have to fetch the WW source (download the archive, git-clone it…), then type in a terminal, within the directory containing the sources:

pip install .

What can I do with WW?

First of all, there is absolutely nothing you can do with WW that you can’t do with simple Python.

In fact, WW simply provides you a set of convenient tools to ease the usage of Python primitives and better exploit the wonders of the standard library.

So, what you can do is basically use those wrappers like you would do with primitive types, and use advanced functions like they always were part of those objects.

Let’s take a simple example. Like every Python programmer, you like lists. Lists are awesome. And adding things to lists is the most natural thing in the world. That’s so natural that no one could blame you for adding multiple things to a list.

>>> lst = [1, 2]
>>> lst.append(3)
>>> lst.append(4)

If you’re new to Python, you may have tried something like this instead:

>>> lst = [1, 2]
>>> lst.append(3, 4).append(5)

Because that would have made perfect sense. And, because of Python is about thinks that make sense, WW gives you this logical syntax. All that you have to do is explicitely tell that you’re using WW wrapper.

>>> from ww import l
>>> lst = l([1, 2]) # wrap your list in the wonderful wrapper
>>> lst.append(3, 4).append(5)
[1, 2, 3, 4, 5]

lst is an instance of <ww.wrappers.lists.ListWrapper>, that we just imported as the simple name l (we’ll use the short name from now). A l object is just a regular Python list, with some bonuses, like this chaining append() method.

From the time you’ll start to use l objects, any method or operator on those objects will start to return other l objects, so you don’t have to wrap them explicitely.

WW has many wrappers:

  • l for lists
  • t for tuples
  • g for iterables
  • d for dictionaries
  • s for strings

They all come with transparent compatibility with builtin Python types, and add cool features.

Right now you’ll find some features are missing or undocumented, since we are still in version 0.1. But it’s progressing steadily.

Add features to strings with s()

ww contains convenient wrappers around strings. The Most important one is StringWrapper, that you will mostly use as the “s()” object.

It behaves like unicode strings (the API is compatible), but make small improvements to the existing methods and add some new methods.

It doesn’t accept bytes as an input. If you do so and it works, you must know it’s not a supported behavior and may change in the future. Only pass:

  • unicode objects in Python 2;
  • str objects in Python 3.

Example

Import:

>>> from ww import s

You always have the more explicit import at your disposal:

>>> from ww.wrappers.strings import StringWrapper

s is just an alias of StringWrapper, but it’s what most people will want to use most of the time. Hence it’s what we will use in the examples.

Basic usages:

>>> string = s("this is a test")
>>> string
u'this is a test'
>>> type(string)
<class 'ww.wrappers.strings.StringWrapper'>
>>> string.upper() # regular string methods are all there
u'THIS IS A TEST'
>>> string[:4] + "foo" # same behaviors you expect from a string
u'thisfoo'

Some existing methods, while still compatible with the previous behavior, have been improved:

>>> string.replace('e', 'a') # just as before
u'this is a tast'
>>> string.replace(('e', 'i'), ('a', 'o')) # and a little more
u'thos os a tast'
>>> s('-').join(range(10))  # join() autocast to string
u'0-1-2-3-4-5-6-7-8-9'
>>> s('-').join(range(10), template="{:.2f}")
u'0.00-1.00-2.00-3.00-4.00-5.00-6.00-7.00-8.00-9.00'

Some methods have been added:

>>> print(s('''
... This should be over indented.
... But it will not be.
... Because dedent() calls textwrap.dedent() on the string.
... ''').dedent())

This should be over indented.
But it will not be.
Because dedent() calls textwrap.dedent() on the string.

By overriding operators, we can provide some interesting syntaxic sugar, such as this shortcut for writting long dedented text:

>>> print(s >> '''
... Calling dedent() is overrated.
... Overriding __rshift__ is much more fun.
... ''')

Calling dedent() is overrated.
Overriding __rshift__ is much more fun.

Also we hacked something that looks like Python 3.6 f-string, but that works in Python 2.7 and 3.3+:

>>> from ww import f
>>> a = 1
>>> f('Sweet, I can print locals: {a}')
u'Sweet, I can print locals: 1'
>>> print(f >> '''
... Yes it works with long string too.
... And globals, if you are into that kind
... of things.
... But we have only {a} for now.
... ''')

Yes it works with long string too.
And globals, if you are into that kind
of things.
But we have only 1 for now.

Warning

Remember that, while f-strings are interpreted at parsing time, our implementation is executed at run-time, making it vulnerable to code injection. This makes it a dangerous feature to put in production.

There is much, much more to play with. Check it out :)

You’ll find bellow the detailed documentation for each method of StringWrapper. Go have a look, there is some great stuff here!

class ww.wrappers.strings.StringWrapper[source]

Convenience wrappers around strings behaving like unicode strings, but make small improvements to the existing methods and add some new methods.

It doesn’t accept bytes as an input. If you do so and it works, you must know it’s not a supported behavior and may change in the future. Only pass:

  • unicode objects in Python 2;
  • str objects in Python 3.

Basic usages:

>>> from ww import s
>>> string = s("this is a test")
>>> string
u'this is a test'
>>> type(string)
<class 'ww.wrappers.strings.StringWrapper'>
>>> string.upper() # regular string methods are all there
u'THIS IS A TEST'
>>> string[:4] + "foo" # same behaviors you expect from a string
u'thisfoo'
>>> string.split(u'a', u'i', u'e')  # lots of features are improved
<IterableWrapper generator>
>>> string.split(u'a', u'i', u'e').list()
[u'th', u's ', u's a t', u'st']
__add__(other)[source]

Concatenate the 2 strings, but wraps it in s().

Parameters:other – The other string to concatenate with the current one.
Raises:TypeError – raised one of the concatenated objects is not a string.
Returns:The concatenated string wrapped in StringWrapper.

Example

>>> from ww import s
>>> s(u'a') + u'b'
u'ab'
>>> type(s(u'a') + u'b')
<class 'ww.wrappers.strings.StringWrapper'>
__getitem__(index)[source]

Make indexing/slicing return s() objects.

Returns:

The result of the indexing/slicing, wrapped in StringWrapper

Raises:
  • IndexError – if the index if greater than the string length.
  • TypeError – if the index is not an integer.

Example

>>> from ww import s
>>> s('Foo')[0]
u'F'
>>> type(s('Foo')[0])
<class 'ww.wrappers.strings.StringWrapper'>
__radd__(other)[source]

Concatenate the 2 strings, s() being on the right of the equation.

Parameters:other – The other string to concatenate with the current one.
Raises:TypeError – raised one of the concatenated objects is not a string.
Returns:The concatenated string wrapped in StringWrapper.

Example

>>> from ww import s
>>> u'b' + s(u'a')
u'ba'
>>> type(u'b' + s(u'a'))
<class 'ww.wrappers.strings.StringWrapper'>
__repr__()[source]

Strings repr always prefixeds with ‘u’ even in Python 3

__weakref__

list of weak references to the object (if defined)

dedent()[source]

Call texwrap.dedent() on the string, removing useless indentation

Returns:The strings without indentation and wrapped with StringWrapper.

Example

>>> from ww import s
>>> print(s('''
...     This should be indented
...     but it will not be
... ''').dedent())

This should be indented
but it will not be
format(*args, **kwargs)[source]

Like str.format(), with f-string features. Returns StringWrapper

s().format() is like str.format() (or unicode.format() in Python 2.7), but returns a StringWrapper.

However, if you don’t pass any argument to it, it will act like f-strings, and look for the variables from the current local context to fill in the markers.

Parameters:
  • *args – elements used to replace {} markers.
  • **kwargs – named element used to replace {name} markers.
Returns:

The formatted string, wrapped in StringWrapper

Example

>>> from ww import s
>>> print(s('Dis ize me, {} !').format('Mario'))
Dis ize me, Mario !
>>> name = 'Mario'
>>> print(s('Dis ize me, {name} !').format())
Dis ize me, Mario !
classmethod from_bytes(byte_string, encoding=None, errors='strict')[source]

Convenience proxy to byte.decode().

This let you decode bytes from the StringWrapper class the same way you would decode it from the bytes class, and wraps the result in StringWrapper.

Parameters:
  • byte_string – encoded text you wish to decode.
  • encoding – the name of the character set you want to use to attempt decoding.
  • errors – the policy to use when encountering error while trying to decode the text. ‘strict’, the default, will raise an exception. ‘ignore’ will skip the faulty bits. ‘replace’ will replace them with ‘?’.
Returns:

The decoded strings wrapped in StringWrapper.

Example

>>> from ww import s
>>> utf8_text = u'Père Noël'.encode('utf8')
>>> print(s.from_bytes(utf8_text, 'utf8'))
Père Noël
>>> type(s.from_bytes(utf8_text, 'utf8'))
<class 'ww.wrappers.strings.StringWrapper'>
>>> print(s.from_bytes(utf8_text, 'ascii', 'replace'))
P��re No��l
>>> print(s.from_bytes(utf8_text, 'ascii', 'ignore'))
Pre Nol
join(iterable, formatter=<function StringWrapper.<lambda>>, template='{}')[source]

Join every item of the iterable into a string.

This is just like the join() method on str() but with auto cast to a string. If you dislike auto cast, formatter and template let you control how to format each element.

Parameters:
  • iterable – the iterable with elements you wish to join.
  • formatter

    a the callable returning a representation of the current element as a string. It will be called on each element, with the element being past as the first parameter and the value of template as the second parameter. The default value is to return:

    template.format(element)
    
  • template

    a string template using the .format() syntax to be used by the formatter callable. The default value is “{}”, so that the formatter can just return:

    "{}".format(element)
    
Returns:

The joined elements as StringWrapper

Example

>>> from ww import s
>>> s('|').join(range(3))
u'0|1|2'
>>> to_string = lambda s, t: str(s) * s
>>> print(s(',').join(range(1, 4), formatter=to_string))
1,22,333
>>> print(s('\n').join(range(3), template='- {}'))
- 0
- 1
- 2
replace(patterns, substitutions, maxreplace=0, flags=0)[source]

Like unicode.replace() but accept several substitutions and regexes

Parameters:
  • patterns – a string, or an iterable of strings to be replaced.
  • substitutions – a string or an iterable of string to use as a replacement. You can pass either one string, or an iterable containing the same number of sustitutions that you passed as patterns. You can also pass a callable instead of a string. It should expact a match object as a parameter.
  • maxreplace – the max number of replacement to make. 0 is no limit, which is the default.
  • flags

    flags you wish to pass if you use regexes. You should pass them as a string containing a combination of:

    • ‘m’ for re.MULTILINE
    • ‘x’ for re.VERBOSE
    • ‘v’ for re.VERBOSE
    • ‘s’ for re.DOTALL
    • ‘.’ for re.DOTALL
    • ‘d’ for re.DEBUG
    • ‘i’ for re.IGNORECASE
    • ‘u’ for re.UNICODE
    • ‘l’ for re.LOCALE
Returns:

The string with replaced bits, wrapped with StringWrapper.

Raises:

ValueError – if you pass the wrong number of substitution.

Example

>>> from __future__ import unicode_literals
>>> from ww import s
>>> s('a,b;c/d').replace((',', ';', '/'), ',')
u'a,b,c,d'
>>> s('a1b33c-d').replace('\d+', ',')
u'a,b,c-d'
>>> s('a-1,b-3,3c-d').replace('[,-]', '', maxreplace=3)
u'a1b3,3c-d'
>>> def upper(match):
...     return match.group().upper()
...
>>> s('a-1,b-3,3c-d').replace('[ab]', upper)
u'A-1,B-3,3c-d'
split(*separators, **kwargs)[source]

Like unicode.split, but accept several separators and regexes

Parameters:
  • separators – strings you can split on. Each string can be a regex.
  • maxsplit – max number of time you wish to split. default is 0, which means no limit.
  • flags

    flags you wish to pass if you use regexes. You should pass them as a string containing a combination of:

    • ‘m’ for re.MULTILINE
    • ‘x’ for re.VERBOSE
    • ‘v’ for re.VERBOSE
    • ‘s’ for re.DOTALL
    • ‘.’ for re.DOTALL
    • ‘d’ for re.DEBUG
    • ‘i’ for re.IGNORECASE
    • ‘u’ for re.UNICODE
    • ‘l’ for re.LOCALE
Returns:

An iterable of substrings.

Raises:
  • ValueError – if you pass a flag without separators.
  • TypeError – if you pass something else than unicode strings.

Example

>>> from ww import s
>>> string = s(u'fat     black cat, big bad dog')
>>> string.split().list()
[u'fat', u'black', u'cat,', u'big', u'bad', u'dog']
>>> string = s(u'a,b;c/d=a,b;c/d')
>>> string.split(u',', u';', u'[/=]', maxsplit=4).list()
[u'a', u'b', u'c', u'd', u'a,b;c/d']
to_bool(default=None)[source]

Take a string with a binary meaning, and turn it into a boolean.

The following strings will be converted:

  • ‘1’ => True,
  • ‘0’ => False,
  • ‘true’ => True,
  • ‘false’ => False,
  • ‘on’ => True,
  • ‘off’ => False,
  • ‘yes’ => True,
  • ‘no’ => False,
  • ‘’ => False
Parameters:default – the value to return if the string can’t be converted.
Returns:A boolean matching the meaning of the string.

Example

>>> from ww import s
>>> s('true').to_bool()
True
>>> s('Off').to_bool()
False
upper()[source]

Call str.upper() on the string, making it uppercase.

Returns:The upper cased string, wrapped in StringWrapper.

Example

>>> from ww import s
>>> print(s('Foo').upper())
FOO
>>> type(s('Foo').upper())
<class 'ww.wrappers.strings.StringWrapper'>

Most of those features are just wrappers around pure functions. We exposed them in “Functional tools for strings” in case you want to use them directly on regular Python strings.

Add features to iterables with g()

IterableWrapper is a convenient wrapper around all iterables. It works withs strings, lists, tuples, dicts, sets, file-like objects, anything declaring __len__ + __getitem__ or __next__, etc. Basically whatever you can apply a for loop to. And it will behave the same way regarding iteration.

It does not assume a size, it does not even assumes the iterable is finite.

When possible IterableWrapper tries to not hold data in memory, but some operations require it, so when in doubt, check the methods doc.

However, remember that reading some iterables, such as generators, will consume them! Make sure this is what you want.

In any case, IterableWrapper improves a lot over the API of regular iterable, providing better chaining, adding shorcuts to itertools, allowing slicing and more.

Warning

g() turns anything into a one-time chain of lazy generators. If you want to keep the underlying behavior of your iterable, g() is not the best choice. You can checkout l(), t(), s(), etc. for wrappers that match g() API but keep the behavior of the matchined data structure.

However, g() has the advantage of working with ANY iterable, no matter the type or the size. Most of its methods are lazy and return a new instance of g().

The other wrappers are specialized and try to mimic the behavior of one particular type: l() for lists, t() for tuples, s() for strings...

Example

Import:

>>> from ww import g

You always have the more explicit import at your disposal:

>>> from ww.wrappers.iterables import IterableWrapper

g is just an alias of IterableWrapper, but it’s what most people will want to use most of the time. Hence it’s what we will use in the examples.

i could have been a better alias for “iterable”, but i is used all the time in Python as a variable name so it would systematically end up shadowed.`g` mnemonic is “generator”.

Basic usages:

>>> from ww import g
>>> gen = g(x * x for x in range(5))
>>> gen
<IterableWrapper generator>
>>> type(gen)
<class 'ww.wrappers.iterables.IterableWrapper'>
>>> for x in gen: # you can iterate on g as expected
...     print(x)
...
0
1
4
9
16

Shortcuts and chaining:

>>> g("azerty").enumerate().sorted().list()
[(0, 'a'), (1, 'z'), (2, 'e'), (3, 'r'), (4, 't'), (5, 'y')]
>>> g(range(3)).join(',') # natural join, with autocast to string
u'0,1,2'

Itertools at your fingertips:

>>> gen = g(x * x for x in range(10))
>>> gen.groupby(lambda x: x % 2).list() # autosort & cast groups
[(0, (0, 4, 16, 36, 64)), (1, (1, 9, 25, 49, 81))]
>>> a, b = g(range(3)).tee(2)
>>> a.list()
[0, 1, 2]
>>> b.list()
[0, 1, 2]
>>> gen = g(range(2)).cycle()
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
0
>>> next(gen)
1

Warning

You can use indexing on g() like you would on list()

Operators to the rescue:

>>> gen = g(range(3)) + "abc"
>>> gen.list()
[0, 1, 2, 'a', 'b', 'c']
>>> gen = g(range(5)) - [0, 3]
>>> gen.list()
[1, 2, 4]
>>> gen = g(range(3)) * 3
>>> gen.list()
[0, 1, 2, 0, 1, 2, 0, 1, 2]

Index all the things!!!

>>> gen = g(x * x for x in range(10))
>>> gen[3:8].list() # slicing uses itertools.islice
[9, 16, 25, 36, 49]
>>> gen = g(x * x for x in range(10))
>>> gen[3]
9
>>> # slicing with a callable will start/stop when first True
>>> g('aeRty')[lambda x: x.isupper():].list()
['R', 't', 'y']

Moar features:

>>> a, b, c = g(range(1)).firsts(3, default="foo")
>>> a
0
>>> b
'foo'
>>> c
'foo'
>>> g(range(10)).chunks(3).list()
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9,)]
>>> # preserve order, works on unsized containers, has a key func
>>> g("azertyazertyazerty").skip_duplicates().list()
['a', 'z', 'e', 'r', 't', 'y']

There is much, much more...

Plus, most feature from this module are actually just delegating the work to the ww.tools.iterable module. It contains stand alone functions for most operations you can apply directly on regular collections.

You may want to use it directly if you wish to not wrap your collections in g().

You’ll find bellow the detailed documentation for each method of IterableWrapper. Go have a look, there is some great stuff here!

class ww.wrappers.iterables.IterableWrapper(iterable, *more_iterables)[source]
__add__(other)[source]

Return a generator that concatenates both generators.

It uses itertools.chain(self, other_iterable), so it works when g() is on the left side of the addition.

Parameters:other – The other iterable to chain with the current one.
Returns:IterableWrapper

Example

>>> from ww import g
>>> (g(range(3)) + "abc").list()
[0, 1, 2, 'a', 'b', 'c']
__getitem__(index)[source]

Act like [x] or [x:y:z] on a generator.

Warning

If you pass an index, it will return the element at this index as it would with any indexable. But it means that if your iterable is a generator, it WILL be consumed immidiatly up to that point.

If you use a slice, it will return a generator and hence only consume your iterable once you start reading it.

Parameters:index – the index of the item to return, or a slice to apply to the iterable.
Raises:IndexError – if the index is bigger than the iterable size.

Example

>>> from ww import g
>>> g(range(3))[1]
1
>>> g(range(3))[4]
Traceback (most recent call last):
...
IndexError: Index "4" out of range
>>> g(range(100))[3:10].list()
[3, 4, 5, 6, 7, 8, 9]
>>> g(range(100))[3:].list() 
[3, 4, 5, 6, 7, 8, 9, ..., 99]
>>> g(range(100))[:10].list()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> g(range(100))[::2].list()
[0, 2, 4, ..., 96, 98]
>>> g(range(100))[::-1]
Traceback (most recent call last):
...
ValueError: The step can not be negative: '-1' given
__init__(iterable, *more_iterables)[source]

Initialize self.iterator to iter(iterable)

If several iterables are passed, they are concatenated.

Parameters:
  • iterable – iterable to use for the iner state.
  • *more_iterables – other iterable to concatenate to the first one.
Returns:

None

Raises:

TypeError – if some arguments are not iterable.

Example

>>> from ww import g
>>> g(range(3)).list()
[0, 1, 2]
>>> g(range(3), "abc").list()
[0, 1, 2, 'a', 'b', 'c']
__iter__()[source]

Return the inner iterator

Example

>>> from ww import g
>>> gen = g(range(10))
>>> iter(gen) == gen.iterator
True
Returns:Inner iterator.
Raises:RuntimeError – if trying call __iter__ after calling .tee()
__mul__(num)[source]

Duplicate itself and concatenate the results.

It’s basically a shortcut for g().chain(*g().tee()).

Parameters:num – The number of times to duplicate.

Example

>>> from ww import g
>>> (g(range(3)) * 3).list()
[0, 1, 2, 0, 1, 2, 0, 1, 2]
>>> (2 * g(range(3))).list()
[0, 1, 2, 0, 1, 2]
__next__(default=None)

Call next() on inner iterable.

Parameters:default – default value to return if there is no next item instead of raising StopIteration.

Example

>>> from ww import g
>>> g(range(10)).next()
0
>>> g(range(0)).next("foo")
'foo'
__radd__(other)[source]

Return a generator that concatenates both iterable.

It uses itertools.chain(other_iterable, self), so it works when g() is on the right side of the addition.

Parameters:other – The other generator to chain with the current one.

Example

>>> from ww import g
>>> ("abc" + g(range(3))).list()
['a', 'b', 'c', 0, 1, 2]
__rmul__(num)

Duplicate itself and concatenate the results.

It’s basically a shortcut for g().chain(*g().tee()).

Parameters:num – The number of times to duplicate.

Example

>>> from ww import g
>>> (g(range(3)) * 3).list()
[0, 1, 2, 0, 1, 2, 0, 1, 2]
>>> (2 * g(range(3))).list()
[0, 1, 2, 0, 1, 2]
__rsub__(other)[source]

Yield items that are not in the other iterable.

Warning

The other iterable will be turned into a set. So make sure:

  • it has a finite size and can fit in memory.
  • you are ok with it being consumed if it’s a generator.
  • it contains only hashable items.
Parameters:other – The other generator to chain with the current one.

Example

>>> from ww import g
>>> (range(5) - g(range(3))).list()
[3, 4]
__sub__(other)[source]

Yield items that are not in the other iterable.

Danger

The other iterable will be turned into a set. So make sure:

  • it has a finite size and can fit in memory.
  • you are ok with it being consumed if it’s a generator.
  • it contains only hashable items.
Parameters:other – The iterable to filter from.

Example

>>> from ww import g
>>> (g(range(6)) - [1, 2, 3]).list()
[0, 4, 5]
__weakref__

list of weak references to the object (if defined)

chunks(size, cast=<class 'tuple'>)[source]

Yield items from an iterator in iterable chunks.

Example:

>>> from ww import g
>>> my_g = g(range(12))
>>> chunks = my_g.chunks(3)
>>> print(type(chunks))
<class 'ww.wrappers.iterables.IterableWrapper'>
>>> chunks = chunks.list()
>>> chunks[0]
(0, 1, 2)
>>> chunks[1]
(3, 4, 5)
copy()[source]

Return an exact copy of the iterable.

The reference of the new iterable will be the same as the source when copy() was called.

Example

>>> from ww import g
>>> my_g_1 = g(range(3))
>>> my_g_2 = my_g_1.copy()
>>> next(my_g_1)
0
>>> next(my_g_1)
1
>>> next(my_g_2)
0
count()[source]

Return the number of elements in the iterable.

Example

>>> from ww import g
>>> g(range(3)).count()
3
cycle()[source]

Create an infinite loop, chaining the iterable on itself

Example

>>> from ww import g
>>> gen = g(range(2)).cycle()
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
0

Warning

Do not attempt a for loop on the result of cycle() unless you really know what you are doing. Cycle will loop forever. Remember you can slice g() objects.

enumerate(start=0)[source]

Give you the position of each element as you iterate.

Parameters:start – the number to start counting from. Default is 0.
Returns:An IterableWrapper, yielding (position, element)

Example

>>> from ww import g
>>> my_g = g('cheese')
>>> my_g.enumerate().list()
[(0, 'c'), (1, 'h'), (2, 'e'), (3, 'e'), (4, 's'), (5, 'e')]
>>> g('cheese').enumerate(start=1).list()
[(1, 'c'), (2, 'h'), (3, 'e'), (4, 'e'), (5, 's'), (6, 'e')]
firsts(items=1, default=None)[source]

Lazily return the first x items from this iterable or default.

Example:

>>> from ww import g
>>> my_g = g(range(12))
>>> my_g.firsts(3).list()
[0, 1, 2]
groupby(keyfunc=None, reverse=False, cast=<class 'tuple'>)[source]

Group items according to one common feature.

Create a generator yielding (group, grouped_items) pairs, with “group” being the return value of keyfunc, and grouped_items being an iterable of items maching this group.

Unlike itertools.groupy, the iterable is automatically sorted for you, also using the keyfunc, since this is what you mostly want to do anyway and forgetting to sort leads to useless results.

Parameters:
  • keyfunc – A callable that must accept the current element to group and return the object you wish to use to determine in which group the element belongs to. This object will also be used for the sorting.
  • reverse – If True, the iterable is sorted in the descending order instead of ascending. Default is False. You probably don’t need to use this, we provide it just in case there is an edge case we didn’t think about.
  • cast – A callable used to choose the type of the groups of items. The default is to return items grouped as a tuple. If you want groups to be generators, pass an identity function such as lambda x: x.
Returns:

An IterableWrapper, yielding (group, grouped_items)

Example

>>> from ww import g
>>> my_gen = g(['morbier', 'cheddar', 'cantal', 'munster'])
>>> my_gen.groupby(lambda i: i[0]).list()
[('c', ('cheddar', 'cantal')), ('m', ('morbier', 'munster'))]
>>> my_gen = g(['morbier', 'cheddar', 'cantal', 'munster'])
>>> my_gen.groupby(len, cast=list).list()
[(6, ['cantal']), (7, ['morbier', 'cheddar', 'munster'])]
join(joiner, formatter=<function IterableWrapper.<lambda>>, template='{}')[source]

Join every item of the iterable into a string. This is just like the join() method on str(), but conveniently stored on the iterable itself.

Example

>>> from ww import g
>>> g(range(3)).join('|')
u'0|1|2'
>>> to_string = lambda s, t: str(s) * s
>>> print(g(range(1, 4)).join(',', formatter=to_string))
1,22,333
>>> print(g(range(3)).join('\n', template='- {}'))
- 0
- 1
- 2
lasts(items=1, default=None)[source]

Lazily return the lasts x items from this iterable or default.

Example:

>>> from ww import g
>>> my_g = g(range(12))
>>> my_g.lasts(3).list()
[9, 10, 11]
map(callable)[source]

Apply map() then wrap the result in g()

Parameters:call – the callable to pass to map()

Example

>>> from ww import g
>>> g(range(3)).map(str).list()
['0', '1', '2']
next(default=None)[source]

Call next() on inner iterable.

Parameters:default – default value to return if there is no next item instead of raising StopIteration.

Example

>>> from ww import g
>>> g(range(10)).next()
0
>>> g(range(0)).next("foo")
'foo'
skip_duplicates(key=<function IterableWrapper.<lambda>>, fingerprints=None)[source]

Yield unique values.

Returns a generator that will yield all objects from iterable, skipping duplicates.

Duplicates are identified using the key function to calculate a unique fingerprint. This does not use natural equality, but the result use a set() to remove duplicates, so defining __eq__ on your objects would have no effect.

By default the fingerprint is the object itself, which ensure the functions works as-is with an iterable of primitives such as int, str or tuple.

Example:
>>> list(skip_duplicates([1, 2, 3, 4, 4, 2, 1, 3 , 4]))
[1, 2, 3, 4]

The return value of key MUST be hashable, which means for non hashable objects such as dict, set or list, you need to specify a function that returns a hashable fingerprint.

Example:
>>> list(skip_duplicates(([], [], (), [1, 2], (1, 2)),
...                      lambda x: tuple(x)))
[[], [1, 2]]
>>> list(skip_duplicates(([], [], (), [1, 2], (1, 2)),
...                      lambda x: (type(x), tuple(x))))
[[], (), [1, 2], (1, 2)]

For more complex types, such as custom classes, the default behavior is to remove nothing. You MUST provide a key function is you wish to filter those.

Example:
>>> class Test(object):
...    def __init__(self, foo='bar'):
...        self.foo = foo
...    def __repr__(self):
...        return "Test('%s')" % self.foo
...
>>> list(skip_duplicates([Test(), Test(), Test('other')]))
[Test('bar'), Test('bar'), Test('other')]
>>> list(skip_duplicates([Test(), Test(), Test('other')],                                         lambda x: x.foo))
[Test('bar'), Test('other')]
sorted(keyfunc=None, reverse=False)[source]

Sort the iterable.

Warning

This will load the entire iterable in memory. Remember you can slice g() objects before you sort them. Also remember you can use callable in g() object slices, making it easy to start or stop iteration on a condition.

Parameters:
  • keyfunc – A callable that must accept the current element to sort and return the object used to determine it’s position. Default to return the object itselt.
  • reverse – If True, the iterable is sorted in the descending order instead of ascending. Default is False.
Returns:

The sorted iterable.

Example

>>> from ww import g
>>> animals = ['dog', 'cat', 'zebra', 'monkey']
>>> for animal in g(animals).sorted():
...     print(animal)
cat
dog
monkey
zebra
>>> for animal in g(animals).sorted(reverse=True):
...     print(animal)
zebra
monkey
dog
cat
>>> for animal in g(animals).sorted(lambda animal: animal[-1]):
...     print(animal)
zebra
dog
cat
monkey
tee(num=2)[source]

Return copies of this generator.

Proxy to itertools.tee().

If you want to concatenate the results afterwards, use g() * x instead of g().tee(x) which does that for you.

Args:
num: The number of returned generators.

Example:

>>> from ww import g
>>> a, b, c = g(range(3)).tee(3)
>>> [tuple(a), tuple(b), tuple(c)]
[(0, 1, 2), (0, 1, 2), (0, 1, 2)]
window(size=2, cast=<class 'tuple'>)[source]

Yield items using a sliding window.

Wield chunks of a given size, but rolling only one item in and out at a time when iterating.

Example

>>> from ww import g
>>> my_g = g(range(12))
>>> my_window = my_g.window(3).list()
>>> my_window[0]
(0, 1, 2)
>>> my_window[1]
(1, 2, 3)
zip(*others)[source]

Apply zip() then wrap in g()

Parameters:others – the iterables to pass to zip()

Example

>>> from ww import g
>>> for element in g(range(3)).zip("abc"):
...     print(*element)
0 a
1 b
2 c
>>> for element in g(range(3)).zip("abc", [True, False, None]):
...    print(*element)
0 a True
1 b False
2 c None

Most of those features are just wrappers around pure functions. We exposed them in “Functional tools for iterables” in case you want to use them directly.

Indices and tables