Add user defined __eq__ to itertools.cycle class
As the title suggests, I am requesting an user defined __eq__ method for the itertools.cycle class. A suggestion for how equivalency might work is that __eq__ would return True if two cyclers have the same iterable and are at the same location in their cycle and False in any other condition. Thank you for considering my suggestion, Andres
On Wed, Dec 30, 2020 at 05:30:52PM -0800, Andres Torres wrote:
As the title suggests, I am requesting an user defined __eq__ method for the itertools.cycle class. A suggestion for how equivalency might work is that __eq__ would return True if two cyclers have the same iterable and are at the same location in their cycle and False in any other condition.
When would you use this proposed feature? Can you give an example of code that would need to check for cycle equality? There's also a bit of a problem with the idea of "the same iterable". Do you mean literally "the same" in the sense of identity, `is`? If so, then these two cycles will not be equal: a = cycle([1, 2, 3]) b = cycle([1, 2, 3]) a == b # always False even though they give the same values in the same order and are at the same point in their cycles. But if you mean "the same" in the sense of equality you have the opposite problem, because iterator equality rightly can only check for identity. E.g. it's not practical for the interpreter to recognise that these two generators are equal, even though the human reader can: (s[0] for s in ('alpaca', 'buffalo', 'camel', 'dugong')) (c.lower() for c in 'DCBA'[::-1]) In general, iterators can only compare by identity, so will miss "equal but distinct" iterators: iter("abc") iter("abc") But there's a bigger issue with the concept of cycle equality. Consider this case: it = iter([1, 2, 3]) a = cycle(it) b = cycle(it) a == b # True by your proposal By your definition the cycles are equal. They both have the same iterable (whether by equality or identity) and they are both in the same state. So far so good. But look at the values they produce: >>> print(next(a), next(b)) 1 2 >>> print(next(a), next(b)) 3 2 >>> print(next(a), next(b)) 1 2 >>> print(next(a), next(b)) 3 2 So we have two equal cycle objects that nevertheless generate radically different values. Ouch. -- Steve
Thank you for reviewing my proposal. First and foremost, let me address the use case for this feature. I have an `SQL` database where I store `itertools.cycle` objects (via pickling) and I use SQLAlchemy to interact with this database. Whenever I "update" the cycler, by calling `next` on the cycler, SQLAlchemy detects whether or not a change has occurred by comparing the "new" object and the "old" object via the `__eq__` method to determine whether the object needs to be updated in the `SQL` database. Since these objects are the still same via` __eq__` operator, no change is detected thus causing SQLAlchemy to no update the cycler in the database. Here an example of this situation ```python thing = session.get(Thing, 123) # thing.cycler is the itertools.cycle object in the database cycler = thing.cycler value_i_want = next(cycler) # SQLAlchemy only attempts to check if a change has occurred # in the __setattr__ method which is why this line is here thing.cycler = cycler ``` You bring up a good point regarding how to determine iterable equality. For example, with your last example, I had no idea that the same iterable behavior could produce radically different results between cyclers. To be quite honest, I don't really understand what causing this quirky behavior. Perhaps this is more of an issue with SQLAlchemy as opposed to an issue with `itertools.cycle`. Maybe somehow SQLAlchemy can insert a callback function into the __next__ method letting it know that in fact `thing.cycler` was updated and should be updated in the SQL database the next call to `session.commit()` Anyway thank you for all your help and thorough analysis of my request! Andres
participants (2)
-
Andres Torres
-
Steven D'Aprano