Problems tried to change function next()

Asked

Viewed 58 times

1

I need to do this class-related activity, I don’t know much about it yet and I’m a little confused.

Write a Python class with a custom iterator, when the next() function is called, it should return the next prime number (starting at 1).

class NextPrime:
    def __iter__(self):
        return 1

    def __next__(self, num):
        while True:
            num += 1
            while True:
                print(num)
                for i in range(2, num):
                    if num % i == 0:
                        break
                else:
                    break
            break
        return num


n = NextPrime
print(next(n))
print(next(n))

Upshot:

line 20, in <module>
    print(next(n))
TypeError: 'type' object is not an iterator
  • you defined next as a method and is trying to use as a function?

1 answer

4


The first mistake is here:

n = NextPrime

Are you saying that n will receive its own class NextPrime, and not an instance of it. To instantiate the class you should use parentheses:

n = NextPrime()

But that alone is not enough. The documentation describes how to create an iterator, but in summary, there are two methods that should be implemented as follows:

  • __iter__: returns the iterator object itself. That is, it must return self, and not 1 as you did
  • __next__: returns the next value (or throws an exception StopIteration if there are no more values to return)

Both methods do not receive any parameter (only the self, clear-cut).

Another detail is that when the number is 4, your code enters loop infinite (see). So let’s change this algorithm.

The for don’t need to go up to the number, can go to its square root. And with the exception of 2 and 3, all other prime numbers are in the form 6k - 1 or 6k + 1 (that is, are predecessors or successors of a multiple of 6), so I can make a loop that only tests these cases. Remember also that 2 is the only even number that is prime, all other primes are odd, so from 3 I can add 2 to the current number (testing the pairs is a waste of time).

Then it would look like this:

from math import sqrt

class NextPrime:
    def __init__(self):
        self.num = 1 # todos começam em 1

    def __iter__(self):
        return self # retorna o próprio objeto, não o valor

    def __next__(self):
        if self.num in (1, 2): # se for 1 ou 2, o próximo número (2 ou 3) com certeza é primo
            self.num += 1
            return self.num

        # aqui eu já sei que "num" é pelo menos 3
        while True:
            self.num += 2 # somo 2, pois não preciso testar números pares
            if self.num % 3 != 0: # se for divisível 3, nem testa o número
                # não é múltiplo de 3, testar se é divisor de 6k - 1 ou 6k + 1
                for i in range(5, int(sqrt(self.num)) + 1, 6):
                    if self.num % i == 0 or self.num % (i + 2) == 0:
                        break # não é primo, testa o próximo
                else: # é primo, interrompe o while
                    break
        return self.num

n = NextPrime()
# imprime os 100 primeiros números primos
for _ in range(100):
    print(next(n))

Remembering that this iterator is infinite, because we do not define a stop condition (could have a maximum limit, and launch the StopIteration case self.num exceed this limit, for example). That is to say, nay do for i in n, but will go into loop infinite.

Another detail is that the first cousin returned is 2, since number 1 is not prime.


Another way to do it would be to create a Generator:

from math import sqrt

def next_prime(start = 1):
    num = start
    if num == 1:
        num += 1
        yield num
    if num == 2:
        num += 1
        yield num

    while True:
        num += 2 # somo 2, pois não preciso testar números pares
        if num % 3 != 0: # se for divisível 3, nem testa o número
            # não é múltiplo de 3, testar se é divisor de 6k - 1 ou 6k + 1
            for i in range(5, int(sqrt(num)) + 1, 6):
                if num % i == 0 or num % (i + 2) == 0:
                    break # não é primo, testa o próximo
            else: # é primo, retorna
                yield num

n = next_prime()
# imprime os 100 primeiros números primos
for _ in range(100):
    print(next(n))

Browser other questions tagged

You are not signed in. Login or sign up in order to post.