0
counter = Counter()
// fill data into counter
for a, b in counter.most_common():
    if (b > 1):
        counter[a] = np.log(b)
    else:
        counter[a] = -np.log((1 / (b+0.01)))

As I see, this is safe, based on my trial. No bad thing happens when I change the collection while I am enumerating it. In other languages, in each cycle of the for, the counter.most_common() value is evaluated.

Doesn't this happen in Python as well?

martineau
  • 119,623
  • 25
  • 170
  • 301
Infinite Possibilities
  • 7,415
  • 13
  • 55
  • 118
  • 2
    "In other languages, in each cycle of the for, the counter.most_common() value is evaluated" - what languages do that? I don't know of any. In every language I know of with a for-each loop, the thing you're looping over is only evaluated once. – user2357112 Feb 15 '17 at 20:30
  • 1
    Or in short: Don't confuse for-each with C-style `for` loops. All languages with a for-each-loop that I know of, evaluate the iterable once at the start. The difference with e.g. Java iterators is that `.most_common()` returns a fresh list that does not reference the original counter. – dhke Feb 15 '17 at 20:32
  • 1
    You might be thinking of cases where you're looping over a lazy object that generates values as needed, or you might be mistaking the effects of mutating the collection for reevaluating it, but `most_common` eagerly creates a new list independent of the counter it was created from. – user2357112 Feb 15 '17 at 20:32
  • It's not allowed to modify a `set` during iteration however this will also raise a `RuntimeError`: `RuntimeError: Set changed size during iteration`. As your operation does not change the size of the counter and is also not raising an exception I'd assume it's safe. – a_guest Feb 15 '17 at 20:34
  • `most_common()` returns a list of the _n_ most common elements at the time it's called, namely once at the beginning of the `for` loop. The values returned may no longer be the current most common ones since you're changing the `Counter` object inside the loop. It might be possible to suggest an alternative or workaround, if you would [edit] your question and explain what you're ultimately trying to achieve. – martineau Feb 15 '17 at 21:05
  • Documentation of the ['for' statement](https://docs.python.org/2/reference/compound_stmts.html#the-for-statement) explains that "The expression list is evaluated once; it should yield an iterable object." In this case the "expression list" is the call to the `counter.most_common()` method. – martineau Feb 15 '17 at 21:15
  • It is safe in your case as you are not iterating over the actual `Counter` but over a list returned by `Counter.most_common` instead. So the collection you are iterating over is different from the collection you modify. In general it is safe to modify a collection you are iterating over as long as you _don't change its size_. – a_guest Feb 15 '17 at 21:39

1 Answers1

0

No, it doesn't. A more illustrative example:

def example():
    print("Ping")
    return [1,2,3,4]

for x in example():
    print(x)

Output:

Ping
1
2
3
4
Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96