Home > Enterprise >  Difference between class instance method and generator
Difference between class instance method and generator

Time:09-23

You need to create a Bingo generator in which the size can be specified.

bg = Bingo(size)

Once created, every time a ball is taken out, the ball number must be provided, what are the balls that have been taken out since the start and how many balls are remaining.

>>>> next(bg)
>>>> (60, [60], 74)

I have the below class

class Bingo:
    def __init__(self, n):
        self.initial_size = n
        self.current_ball = None
        self.picked_balls = []
        self.remainder_balls = n

    def pick_ball(self):
        # Do not consider the already picked balls for choosing randomly
        self.current_ball = \
            random.choice([i for i in range(1, self.initial_size)
                           if i not in self.picked_balls])
        self.picked_balls.append(self.current_ball)
        self.remainder_balls -= 1

In order to extract a ball from my Bingo, I would need to do the below:

bg = Bingo(100)
bg.pick_ball()

However, I have been asked to do it this way

bg = Bingo(100)
pick_ball(bg) 

with a generator, so I was wondering if that was possible with the class itself or if it is a completely different approach. Any help will be hugely appreciated.

CodePudding user response:

Here's an actual generator solution. I kept the requested name as-is, although it should normally be bingo.

import random

def Bingo(n):
    taken = []
    for _ in range(n):
        take = random.choice(list(set(range(1, n 1)) - set(taken)))
        taken.append(take)
        yield take, taken, n - len(taken)

bg = Bingo(75)
print(next(bg))
print(next(bg))
print(next(bg))

Sample output (Try it online!):

(19, [19], 74)
(30, [19, 30], 73)
(52, [19, 30, 52], 72)

The task description is not concerned about efficiency, but here comes an efficient one anyway. The above solution is already more efficient than yours, taking O(n) time for each pick instead of O(n2). The below solution takes only O(1):

def Bingo(n):
    remaining = list(range(1, n 1))
    taken = []
    while remaining:
        i = random.randrange(len(remaining))
        taken.append(remaining[i])
        remaining[i] = remaining[-1]
        remaining.pop()
        yield taken[-1], taken, len(remaining)

Try it online!

CodePudding user response:

You can make your class implement the iterator protocol and then use it in your function:

class Bingo:
    def __init__(self, n):
        # ...

    def __next__(self):
        if self.remainder_balls <= 0:
            raise StopIteration
        self.current_ball = # ...
        self.remainder_balls -= 1
        return self.current_ball

    # this is not strictly required for your use case, but makes 
    # it iterable as well, as in: `for ball in bingo:`
    def __iter__(self):  
        return self

def pick_ball(bingo):
    return next(bingo)
  • Related