Python modifying wrong list?

2019-08-06 11:58发布

问题:

I'm trying to generate a list of primes using the this method. I need to loop through every number 2...n and check it for multiples of 2...n. For some reason, the wrong list seems to be getting modified.

import sys
import argparse
import math

parser = argparse.ArgumentParser(description='find the largest prime factor of a number')
parser.add_argument('n', type=int, help='number')
args = parser.parse_args()

sieve = []
for i in range(2,args.n+1): sieve.append(i) # tried int(i)


copy1 = sieve # tried making extra copies. . .
copy2 = sieve
copy3 = sieve
#print int(math.sqrt(args.n))

for index, i in enumerate(copy1):
    #print index, i
    for ii in copy2:
        #print ii
        if i % ii == 0:
            sieve[index]= None


print sieve

I get the following error:

Traceback (most recent call last):  
  File "3.py", line 22, in <module>
    if i % ii == 0: TypeError: unsupported operand type(s) for %:
'int' and 'str'

回答1:

You're not making copies. You're using references, so copy1, copy2, and copy3 all refer to the same list -- sieve. If you want to copy, use:

copy1 = sieve[:]

which will create a copy of sieve and assign it to copy1.



回答2:

You need to use

copy1 = sieve[:] # tried making extra copies. . .
copy2 = sieve[:]
copy3 = sieve[:]

to actually copy the list. Otherwise you just copy the reference to the list.

>>> a = [1,2]
>>> b = a
>>> c = a[:]
>>> b[0] = 0
>>> c[0] = 3
>>> a
[0, 2]
>>> b
[0, 2]
>>> c
[3, 2]


回答3:

copy1 = sieve
copy2 = sieve
copy3 = sieve

These are not copies they are reference's.

primes = [2,3,5,7]

def is_prime(n):
    if n in primes:
        return True
    for item in primes:
        if n % item == 0:
            return False
    return True

assert is_prime(4) == False
assert is_prime(29) == True
assert is_prime(65) == False

Is a good sieve method

More unit testing because its fun

true_primes = [int(item) for item in '11,13,17,19,23,29,31,37,41,43,47'.split(',')]
for item in xrange(10, 50):
    if is_prime(item) == True:
        assert item in true_primes
    else:
        assert item not in true_primes


回答4:

Python has reference semantics. In general, a = b causes the name a to refer to the same value that the name b currently refers to. It does not create or store a new value.

You can clone a list with the [:] trick that was mentioned. A more general-purpose solution for copying things is to use the copy module.

However, good Python code normally does not require explicitly copying things very often. You should get familiar with using list comprehensions to create "modified versions" of existing sequences. For example, we can implement the sieve as (showing off a few other things as well):

import sys, argparse, math, itertools

parser = argparse.ArgumentParser(description='find the largest prime factor of a number')
parser.add_argument('n', type=int, help='number')
args = parser.parse_args()

# Using a loop over a 'range' to fill a list with increasing values is silly, because
# the range *is* a list of increasing values - that's how the 'for i in ...' bit works.
sieve = range(2, args.n + 1)

# We don't need to remember the original loop at all.
# Instead, we rely on each iteration of the sieve putting a new prime at the beginning.
for index in itertools.count(): # Counting upward,
  if index >= len(sieve): break # until we run out of elements,
  prime = sieve[index] # we grab the next prime from the list,
  sieve = [x for x in sieve if x == prime or x % prime != 0] # and sieve the list with it.
# Of course, we can optimize that by checking that prime < sqrt(args.n), or whatever.

print sieve


回答5:

I couldn't test this, because I don't have a copy of Python 3.2 (argparse is new in Python 3.2), but two obvious things come to mind:

First, you do need to do sieve.append(int(i)) .

Second, you aren't making a copy of sieve, you are simply creating a new reference to the same list. To make a copy, you need to

copy1 = sieve[:]