diff --git a/cycler.py b/cycler.py
index cd17aa5..d6467ff 100644
--- a/cycler.py
+++ b/cycler.py
@@ -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)
@@ -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)
@@ -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
@@ -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__")
@@ -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
@@ -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):
@@ -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):
@@ -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
+ 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
@@ -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)
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 5da5160..e8b3a4c 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -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:
@@ -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
@@ -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
@@ -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
~~~~~~~~~~~~~~
@@ -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
@@ -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)
@@ -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))
@@ -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
diff --git a/test_cycler.py b/test_cycler.py
index a48b6df..3a1b426 100644
--- a/test_cycler.py
+++ b/test_cycler.py
@@ -24,16 +24,18 @@ def _cycles_equal(c1, c2):
def test_creation():
- c = cycler('c', 'rgb')
+ c = cycler(c='rgb')
yield _cycler_helper, c, 3, ['c'], [['r', 'g', 'b']]
- c = cycler('c', list('rgb'))
+ c = cycler(c=list('rgb'))
+ yield _cycler_helper, c, 3, ['c'], [['r', 'g', 'b']]
+ c = cycler(cycler(c='rgb'))
yield _cycler_helper, c, 3, ['c'], [['r', 'g', 'b']]
def test_compose():
- c1 = cycler('c', 'rgb')
- c2 = cycler('lw', range(3))
- c3 = cycler('lw', range(15))
+ c1 = cycler(c='rgb')
+ c2 = cycler(lw=range(3))
+ c3 = cycler(lw=range(15))
# addition
yield _cycler_helper, c1+c2, 3, ['c', 'lw'], [list('rgb'), range(3)]
yield _cycler_helper, c2+c1, 3, ['c', 'lw'], [list('rgb'), range(3)]
@@ -54,75 +56,87 @@ def test_compose():
def test_inplace():
- c1 = cycler('c', 'rgb')
- c2 = cycler('lw', range(3))
+ c1 = cycler(c='rgb')
+ c2 = cycler(lw=range(3))
c2 += c1
yield _cycler_helper, c2, 3, ['c', 'lw'], [list('rgb'), range(3)]
- c3 = cycler('c', 'rgb')
- c4 = cycler('lw', range(3))
+ c3 = cycler(c='rgb')
+ c4 = cycler(lw=range(3))
c3 *= c4
target = zip(*product(list('rgb'), range(3)))
yield (_cycler_helper, c3, 9, ['c', 'lw'], target)
def test_constructor():
- c1 = cycler('c', 'rgb')
- c2 = cycler('ec', c1)
+ c1 = cycler(c='rgb')
+ c2 = cycler(ec=c1)
yield _cycler_helper, c1+c2, 3, ['c', 'ec'], [['r', 'g', 'b']]*2
- c3 = cycler('c', c1)
+ c3 = cycler(c=c1)
yield _cycler_helper, c3+c2, 3, ['c', 'ec'], [['r', 'g', 'b']]*2
+ # Using a non-string hashable
+ c4 = cycler(1, range(3))
+ yield _cycler_helper, c4+c1, 3, [1, 'c'], [range(3), ['r', 'g', 'b']]
+
+ # addition using cycler()
+ yield (_cycler_helper, cycler(c='rgb', lw=range(3)),
+ 3, ['c', 'lw'], [list('rgb'), range(3)])
+ yield (_cycler_helper, cycler(lw=range(3), c='rgb'),
+ 3, ['c', 'lw'], [list('rgb'), range(3)])
+ # Purposely mixing them
+ yield (_cycler_helper, cycler(c=range(3), lw=c1),
+ 3, ['c', 'lw'], [range(3), list('rgb')])
def test_failures():
- c1 = cycler('c', 'rgb')
- c2 = cycler('c', c1)
+ c1 = cycler(c='rgb')
+ c2 = cycler(c=c1)
assert_raises(ValueError, add, c1, c2)
assert_raises(ValueError, iadd, c1, c2)
assert_raises(ValueError, mul, c1, c2)
assert_raises(ValueError, imul, c1, c2)
- c3 = cycler('ec', c1)
+ c3 = cycler(ec=c1)
- assert_raises(ValueError, cycler, 'c', c2 + c3)
+ assert_raises(ValueError, cycler, c=c2+c3)
def test_simplify():
- c1 = cycler('c', 'rgb')
- c2 = cycler('ec', c1)
+ c1 = cycler(c='rgb')
+ c2 = cycler(ec=c1)
for c in [c1 * c2, c2 * c1, c1 + c2]:
yield _cycles_equal, c, c.simplify()
def test_multiply():
- c1 = cycler('c', 'rgb')
+ c1 = cycler(c='rgb')
yield _cycler_helper, 2*c1, 6, ['c'], ['rgb'*2]
- c2 = cycler('ec', c1)
+ c2 = cycler(ec=c1)
c3 = c1 * c2
yield _cycles_equal, 2*c3, c3*2
def test_mul_fails():
- c1 = cycler('c', 'rgb')
+ c1 = cycler(c='rgb')
assert_raises(TypeError, mul, c1, 2.0)
assert_raises(TypeError, mul, c1, 'a')
assert_raises(TypeError, mul, c1, [])
def test_getitem():
- c1 = cycler('lw', range(15))
+ c1 = cycler(3, range(15))
widths = list(range(15))
for slc in (slice(None, None, None),
slice(None, None, -1),
slice(1, 5, None),
slice(0, 5, 2)):
- yield _cycles_equal, c1[slc], cycler('lw', widths[slc])
+ yield _cycles_equal, c1[slc], cycler(3, widths[slc])
def test_fail_getime():
- c1 = cycler('lw', range(15))
+ c1 = cycler(lw=range(15))
assert_raises(ValueError, Cycler.__getitem__, c1, 0)
assert_raises(ValueError, Cycler.__getitem__, c1, [0, 1])
@@ -135,24 +149,25 @@ def _repr_tester_helper(rpr_func, cyc, target_repr):
def test_repr():
- c = cycler('c', 'rgb')
- c2 = cycler('lw', range(3))
+ c = cycler(c='rgb')
+ # Using an identifier that would be not valid as a kwarg
+ c2 = cycler('3rd', range(3))
- c_sum_rpr = "(cycler('c', ['r', 'g', 'b']) + cycler('lw', [0, 1, 2]))"
- c_prod_rpr = "(cycler('c', ['r', 'g', 'b']) * cycler('lw', [0, 1, 2]))"
+ c_sum_rpr = "(cycler('c', ['r', 'g', 'b']) + cycler('3rd', [0, 1, 2]))"
+ c_prod_rpr = "(cycler('c', ['r', 'g', 'b']) * cycler('3rd', [0, 1, 2]))"
yield _repr_tester_helper, '__repr__', c + c2, c_sum_rpr
yield _repr_tester_helper, '__repr__', c * c2, c_prod_rpr
- sum_html = "
"
- prod_html = "'c' | 'lw' | 'r' | 0 |
'r' | 1 |
'r' | 2 |
'g' | 0 |
'g' | 1 |
'g' | 2 |
'b' | 0 |
'b' | 1 |
'b' | 2 |
"
+ sum_html = ""
+ prod_html = "'3rd' | 'c' | 0 | 'r' |
1 | 'r' |
2 | 'r' |
0 | 'g' |
1 | 'g' |
2 | 'g' |
0 | 'b' |
1 | 'b' |
2 | 'b' |
"
yield _repr_tester_helper, '_repr_html_', c + c2, sum_html
yield _repr_tester_helper, '_repr_html_', c * c2, prod_html
def test_call():
- c = cycler('c', 'rgb')
+ c = cycler(c='rgb')
c_cycle = c()
assert_true(isinstance(c_cycle, cycle))
j = 0
@@ -171,14 +186,14 @@ def _eq_test_helper(a, b, res):
def test_eq():
- a = cycler('c', 'rgb')
- b = cycler('c', 'rgb')
+ a = cycler(c='rgb')
+ b = cycler(c='rgb')
yield _eq_test_helper, a, b, True
yield _eq_test_helper, a, b[::-1], False
- c = cycler('lw', range(3))
+ c = cycler(lw=range(3))
yield _eq_test_helper, a+c, c+a, True
yield _eq_test_helper, a+c, c+b, True
yield _eq_test_helper, a*c, c*a, False
yield _eq_test_helper, a, c, False
- d = cycler('c', 'ymk')
+ d = cycler(c='ymk')
yield _eq_test_helper, b, d, False