Skip to content

Expand cycler() signature. #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 21, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 75 additions & 16 deletions cycler.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
You can add cyclers::

from cycler import cycler
cc = (cycler('color', list('rgb')) +
cycler('linestyle', ['-', '--', '-.']))
cc = (cycler(color=list('rgb')) +
cycler(linestyle=['-', '--', '-.']))
for d in cc:
print(d)

Expand All @@ -22,8 +22,8 @@
You can multiply cyclers::

from cycler import cycler
cc = (cycler('color', list('rgb')) *
cycler('linestyle', ['-', '--', '-.']))
cc = (cycler(color=list('rgb')) *
cycler(linestyle=['-', '--', '-.']))
for d in cc:
print(d)

Expand Down Expand Up @@ -113,8 +113,8 @@ def __init__(self, left, right=None, op=None):
Do not use this directly, use `cycler` function instead.
"""
self._keys = _process_keys(left, right)
self._left = copy.copy(left)
self._right = copy.copy(right)
self._left = copy.deepcopy(left)
self._right = copy.deepcopy(right)
self._op = op

@property
Expand Down Expand Up @@ -164,7 +164,7 @@ def __getitem__(self, key):
# TODO : maybe add numpy style fancy slicing
if isinstance(key, slice):
trans = self._transpose()
return reduce(add, (cycler(k, v[key])
return reduce(add, (_cycler(k, v[key])
for k, v in six.iteritems(trans)))
else:
raise ValueError("Can only use slices with Cycler.__getitem__")
Expand Down Expand Up @@ -203,7 +203,7 @@ def __mul__(self, other):
return Cycler(self, other, product)
elif isinstance(other, int):
trans = self._transpose()
return reduce(add, (cycler(k, v*other)
return reduce(add, (_cycler(k, v*other)
for k, v in six.iteritems(trans)))
else:
return NotImplemented
Expand All @@ -228,11 +228,11 @@ def __iadd__(self, other):
other : Cycler
The second Cycler
"""
old_self = copy.copy(self)
old_self = copy.deepcopy(self)
self._keys = _process_keys(old_self, other)
self._left = old_self
self._op = zip
self._right = copy.copy(other)
self._right = copy.deepcopy(other)
return self

def __imul__(self, other):
Expand All @@ -245,11 +245,11 @@ def __imul__(self, other):
The second Cycler
"""

old_self = copy.copy(self)
old_self = copy.deepcopy(self)
self._keys = _process_keys(old_self, other)
self._left = old_self
self._op = product
self._right = copy.copy(other)
self._right = copy.deepcopy(other)
return self

def __eq__(self, other):
Expand Down Expand Up @@ -329,17 +329,76 @@ def simplify(self):
# I would believe that there is some performance implications

trans = self._transpose()
return reduce(add, (cycler(k, v) for k, v in six.iteritems(trans)))
return reduce(add, (_cycler(k, v) for k, v in six.iteritems(trans)))


def cycler(label, itr):
def cycler(*args, **kwargs):
"""
Create a new `Cycler` object from a single positional argument,
a pair of positional arguments, or the combination of keyword arguments.

cycler(arg)
cycler(label1=itr1[, label2=iter2[, ...]])
cycler(label, itr)

Form 1 simply copies a given `Cycler` object.

Form 2 composes a `Cycler` as an inner product of the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"inner product" might not mean much to many docstring readers here. Maybe add something like, "All iterables are cycled simultaneously."

pairs of keyword arguments. In other words, all of the
iterables are cycled simultaneously, as if through zip().

Form 3 creates a `Cycler` from a label and an iterable.
This is useful for when the label cannot be a keyword argument
(e.g., an integer or a name that has a space in it).

Parameters
----------
arg : Cycler
Copy constructor for Cycler.

label : name
The property key. In the 2-arg form of the function,
the label can be any hashable object. In the keyword argument
form of the function, it must be a valid python identifier.

itr : iterable
Finite length iterable of the property values.

Returns
-------
cycler : Cycler
New `Cycler` for the given property

"""
if args and kwargs:
raise TypeError("cyl() can only accept positional OR keyword "
"arguments -- not both.")

if len(args) == 1:
if not isinstance(args[0], Cycler):
raise TypeError("If only one positional argument given, it must "
" be a Cycler instance.")
return copy.deepcopy(args[0])
elif len(args) == 2:
return _cycler(*args)
elif len(args) > 2:
raise TypeError("Only a single Cycler can be accepted as the lone "
"positional argument. Use keyword arguments instead.")

if kwargs:
return reduce(add, (_cycler(k, v) for k, v in six.iteritems(kwargs)))

raise TypeError("Must have at least a positional OR keyword arguments")


def _cycler(label, itr):
"""
Create a new `Cycler` object from a property name and
iterable of values.

Parameters
----------
label : str
label : hashable
The property key.

itr : iterable
Expand All @@ -357,7 +416,7 @@ def cycler(label, itr):
raise ValueError(msg)

if label in keys:
return copy.copy(itr)
return copy.deepcopy(itr)
else:
lab = keys.pop()
itr = list(v[lab] for v in itr)
Expand Down
31 changes: 21 additions & 10 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ hashable (as it will eventually be used as the key in a :obj:`dict`).
from cycler import cycler


color_cycle = cycler('color', ['r', 'g', 'b'])
color_cycle = cycler(color=['r', 'g', 'b'])
color_cycle

The `Cycler` knows it's length and keys:
Expand All @@ -61,12 +61,12 @@ the label
for v in color_cycle:
print(v)

`Cycler` objects can be passed as the second argument to :func:`cycler`
`Cycler` objects can be passed as the argument to :func:`cycler`
which returns a new `Cycler` with a new label, but the same values.

.. ipython:: python

cycler('ec', color_cycle)
cycler(ec=color_cycle)


Iterating over a `Cycler` results in the finite list of entries, to
Expand Down Expand Up @@ -94,12 +94,12 @@ Equal length `Cycler` s with different keys can be added to get the

.. ipython:: python

lw_cycle = cycler('lw', range(1, 4))
lw_cycle = cycler(lw=range(1, 4))

wc = lw_cycle + color_cycle

The result has the same length and has keys which are the union of the
two input `Cycler` s.
two input `Cycler`'s.

.. ipython:: python

Expand All @@ -123,6 +123,17 @@ As with arithmetic, addition is commutative
for j, (a, b) in enumerate(zip(lw_c, c_lw)):
print('({j}) A: {A!r} B: {B!r}'.format(j=j, A=a, B=b))

For convenience, the :func:`cycler` function can have multiple
key-value pairs and will automatically compose them into a single
`Cycler` via addition

.. ipython:: python

wc = cycler(c=['r', 'g', 'b'], lw=range(3))

for s in wc:
print(s)


Multiplication
~~~~~~~~~~~~~~
Expand All @@ -131,7 +142,7 @@ Any pair of `Cycler` can be multiplied

.. ipython:: python

m_cycle = cycler('marker', ['s', 'o'])
m_cycle = cycler(marker=['s', 'o'])

m_c = m_cycle * color_cycle

Expand Down Expand Up @@ -199,7 +210,7 @@ We can use `Cycler` instances to cycle over one or more ``kwarg`` to
figsize=(8, 4))
x = np.arange(10)

color_cycle = cycler('c', ['r', 'g', 'b'])
color_cycle = cycler(c=['r', 'g', 'b'])

for i, sty in enumerate(color_cycle):
ax1.plot(x, x*(i+1), **sty)
Expand All @@ -219,7 +230,7 @@ We can use `Cycler` instances to cycle over one or more ``kwarg`` to
figsize=(8, 4))
x = np.arange(10)

color_cycle = cycler('c', ['r', 'g', 'b'])
color_cycle = cycler(c=['r', 'g', 'b'])
ls_cycle = cycler('ls', ['-', '--'])
lw_cycle = cycler('lw', range(1, 4))

Expand All @@ -243,14 +254,14 @@ A :obj:`ValueError` is raised if unequal length `Cycler` s are added together
.. ipython:: python
:okexcept:

cycler('c', ['r', 'g', 'b']) + cycler('ls', ['-', '--'])
cycler(c=['r', 'g', 'b']) + cycler(ls=['-', '--'])

or if two cycles which have overlapping keys are composed

.. ipython:: python
:okexcept:

color_cycle = cycler('c', ['r', 'g', 'b'])
color_cycle = cycler(c=['r', 'g', 'b'])

color_cycle + color_cycle

Expand Down
Loading