Python modifying wrong list?
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 in开发者_高级运维t(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'
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
.
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]
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
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
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[:]
精彩评论