Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

221
Visualizações
How can I make an object with an interface like a random number generator, but that actually generates a specified sequence?

I'd like to construct an object that works like a random number generator, but generates numbers in a specified sequence.

# a random number generator
rng = lambda : np.random.randint(2,20)//2

# a non-random number generator
def nrng():
    numbers = np.arange(1,10.5,0.5)
    for i in range(len(numbers)):
        yield numbers[i]

for j in range(10):
    print('random number', rng())
    print('non-random number', nrng())

The issue with the code above that I cannot call nrng in the last line because it is a generator. I know that the most straightforward way to rewrite the code above is to simply loop over the non-random numbers instead of defining the generator. I would prefer getting the example above to work because I am working with a large chunk of code that include a function that accepts a random number generator as an argument, and I would like to add the functionality to pass non-random number sequences without rewriting the entire code.

EDIT: I see some confusion in the comments. I am aware that python's random number generators generate pseudo-random numbers. This post is about replacing a pseudo-random-number generator by a number generator that generates numbers from a non-random, user-specified sequence (e.g., a generator that generates the number sequence 1,1,2,2,1,0,1 if I want it to).

over 4 years ago · Santiago Trujillo
6 Respostas
Responde à pergunta

0

You can call next() with a generator or iterator as an argument to withdraw exactly one element from it. Saving the generator to a variable beforehand allows you to do this multiple times.

# make it a generator
def _rng():
    while True:
        yield np.random.randint(2,20)//2

# a non-random number generator
def _nrng():
    numbers = np.arange(1,10.5,0.5)
    for i in range(len(numbers)):
        yield numbers[i]

rng = _rng()
nrng = _nrng()
for j in range(10):
    print('random number', next(rng))
    print('non-random number', next(nrng))
over 4 years ago · Santiago Trujillo Relatório

0

np.random.randint can remember the last number it generated because it's a function of the np.random.RandomState class. Numpy simply aliases the class method so that it's accessible directly from the np.random module instead of having you access it through the class.

Knowing this, you can write your own class to work like so:

class NotRandom:

    numbers = np.arange(1,10.5,0.5)
    last_index = -1

    @classmethod
    def nrng(cls):
        cls.last_index += 1
        if cls.last_index < len(cls.numbers):
            return cls.numbers[cls.last_index]
        # else:
        return None

# Create an alias to the classmethod
nrng = NotRandom.nrng      # Note this is OUTSIDE the class

Then, you can do:

print(nrng())    # 1.0
print(nrng())    # 1.5
print(nrng())    # 2.0

If you want to be able to have multiple concurrent instances of nrng, you could make nrng() an instance method instead of a class method:

class NotRandom:
    def __init__(self):
        self.numbers = np.arange(1,10.5,0.5)
        self.last_index = -1

    def nrng(self):
        self.last_index += 1
        if self.last_index < len(self.numbers):
            return self.numbers[self.last_index]
        # else:
        return None

# Create an object. Then create an alias to its method
nrng = NotRandom().nrng 

Then, you can use nrng() to refer to the method that's bound to the NotRandom instance you created. If you want another instance, you can have that too:

another_notrandom = NotRandom()
nrng2 = another_notrandom.rng

print(nrng())    # 1.0
print(nrng())    # 1.5

print(nrng2())   # 1.0

print(nrng())    # 2.0

print(nrng2())   # 1.5

over 4 years ago · Santiago Trujillo Relatório

0

Edit:

The cleanest way to do this would be to use a lambda to wrap your call to next(nrng) as per great comment from @GACy20:

def nrng_gen():
    yield from range(10)

nrng = nrng_gen()

nrng_func = lambda: next(nrng)

for i in range(10):
    print(nrng_func())

Original answer:

If you want your object to keep state and look like a function, create a custom class with __call__ method.

eg.

class NRNG:
    def __init__(self):
        self.numbers = range(10)
        self.state = -1
    def __call__(self):
        self.state += 1
        return self.numbers[self.state]
        
nrng = NRNG()


for i in range(10):
    print(nrng())

However, I wouldn't recommend this unless absolutely necessary, as it obscures the fact that your nrng keeps a state (although technically, most rngs keep their state internally).

It's best to just use a regular generator with yield by calling next on it or to write a custom iterator (also class-based). Those will work with things like for loops and other python tools for iteration (like the excellent itertools package).

over 4 years ago · Santiago Trujillo Relatório

0

function that accepts a random number generator as an argument

Call it like this then:

that_function(nrng().__next__)

Or with functools.partial:

that_function(partial(next, nrng()))

Or without your generator, if that arange is all you want:

that_function(iter(np.arange(1,10.5,0.5)).__next__)

Demo code (rng and nrng are yours, I added that_function and the test calls):

import numpy as np
from functools import partial

# a random number generator
rng = lambda : np.random.randint(2,20)//2

# a non-random number generator
def nrng():
    numbers = np.arange(1,10.5,0.5)
    for i in range(len(numbers)):
        yield numbers[i]

def that_function(rng):
    print(*(rng() for j in range(10)))

that_function(rng)
that_function(nrng().__next__)
that_function(iter(np.arange(1,10.5,0.5)).__next__)
that_function(partial(next, nrng()))

Output (Try it online!):

4 6 1 3 8 7 3 6 2 1
1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5
1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5
1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5
over 4 years ago · Santiago Trujillo Relatório

0

The simplest solution to making a generator callable is to make a bound method for __next__:

>>> def f():
        yield 10
        yield 20
        yield 30
    
>>> g = f().__next__
>>> g()
10
>>> g()
20
>>> g()
30
over 4 years ago · Santiago Trujillo Relatório

0

I do agree, next() seems the way to go. Honestly I would have simply done something like this:

# Get the first n numbers (10 in this case)

for i,j in enumerate(nrng()):  
    if i > 9:
        break
    print('random number', rng())
    print('non-random number', j)
    
# Or this if you want to get all numbers in nrng()

for j in nrng():
    print('random number', rng())
    print('non-random number', j)
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda