0

I've been through some answers to similar questions on stackoverflow, whereas I appears to be facing a quite delicate situation wherein some of them won't work. below is my code:

import random
from datetime import datetime
array = ['a', 'b', 'c', 'd', 'e', 'f']
def log():
    print(random.choice(array))

characters_to_remove = ".-:"
now = str(datetime.now()).replace(" ", "")
for character in characters_to_remove:
    now = now.replace(character, "")

# print(now)
time = now
time = int(time)
# print(time)

paramTime = time % 535
# print(paramTime)

# param = int(str(paramTime)[:2])
param = int(str(paramTime)[:1])
# print(param)

def repeat(times, func):
    for i in range(param): func()

print(repeat(param, log))
# print(str(repeat(param, log)).split(""))
# print('----------')

as you can see, the for loop is used in order to call a function (for multiple times):

def repeat(param, func):
    # param stands for the number of times the function is to be called
    for i in range(param): func()

now I want to convert my outputs as a list and ergo be able to get the most commonly outputted value... what do I do?

quamrana
  • 37,849
  • 12
  • 53
  • 71
KnowsCount
  • 187
  • 1
  • 11

4 Answers4

4

There is no good way to convert the output of a print() into values that can be collected.

You just need functions which return values and a loop which collects them:

import random

array = ['a', 'b', 'c', 'd', 'e', 'f']
def choose():
    return random.choice(array)

def repeat(param, func):
    # param stands for the number of times the function is to be called
    return [func() for _ in range(param)]

# ...
# other parts elided

print(repeat(param, choose))

The expected output is a list which you can examine or manipulate further.

Also, see this answer for a way to get the most popular value.

You can use it like this:

def get_max(l):
    return max(l, key=l.count) 

print(get_max(repeat(param,log)))
quamrana
  • 37,849
  • 12
  • 53
  • 71
  • You are so quick! I was about the post the answer but my answer is almost the same as yours! –  Aug 14 '21 at 17:04
  • @Sujay: He he! I expected you to be here. :-) It just goes to show that sometimes there is just one way to solve a problem. – quamrana Aug 14 '21 at 17:05
  • ah I see... that's really stupid of me. I am really new to python cos I am a frontend so I asked this silly question... sorry! – KnowsCount Aug 14 '21 at 17:06
2

You can do the following:

  1. return the value from log function
  2. Use collections.counter to get the most common element.
import collections
....
def log():
    return random.choice(array)

....
def get_max(l):
    return max(collections.Counter(l))
def repeat(times, func):
    return [func() for i in range(times)]

print(get_max(repeat(param,log)))
  • well, appears to me you have yet another solution! – KnowsCount Aug 14 '21 at 17:09
  • 2
    Ok, @Sujay. I think you've gone a bit too far. You should split your `repeat()` into two functions: 1. the `repeat()` that I have, plus 2. A new function which does the `max()` and `Counter()` thing. Then they are both reuseable. – quamrana Aug 14 '21 at 17:09
  • 1
    `max(collections.Counter([1, 1, 1, 2]))` returns 2. – enzo Aug 14 '21 at 17:14
2

You might want to generalize log to print to a given file, not just standard output.

def log(fh=sys.stdout):
    print(random.choice(array), file=fh)

Then you can make multiple calls that write to a single io.StringIO object, from which you can get a single string containing everything written to the file. Splitting that string on newlines may be sufficient.

my_log = io.StringIO()
repeat(param, lambda: log(my_log))
my_log.seek(0)

for x in my_log:
    print(x.strip())

You could also define your own file-like class that appends its arguments to a list. Something like

class Collector:
    def __init__(self):
         self.lines = []
    def write(self, line):
        self.lines.append(line)

    def __iter__(self):
        yield from self.lines

my_log = Collector()
repeat(param, log(my_log))

# my_log.lines is just a list of the strings produced by each call to log
chepner
  • 497,756
  • 71
  • 530
  • 681
  • I actually thought about this and thought it wouldn't be the most elegant of solutions... and so I stackoverflowed it and everything. but hey, at least you prove me smart... somehow... to came to an agreement with people like you. thanks for your time! – KnowsCount Aug 14 '21 at 17:19
1

If you can't change your log function (i.e. it belongs to a third-party library), you could change the value of sys.stdout using contextlib.redirect_stdout:

import io
import contextlib

with contextlib.redirect_stdout(io.StringIO()) as fake_stdout:
    # ...

Change # ... with your calculation and at the end access the outputted values using fake_stdout.getvalue():

import io
import contextlib

with contextlib.redirect_stdout(io.String()) as fake_stdout:
    import random
    
    def log():
        print(random.randint(1, 10))
        
    def repeat(param, func):
        for i in range(param): 
            func()

    repeat(10, log)

print(fake_stdout.getvalue())

The above outputs

6
9
10
10
4
9
10
8
5
3

To convert it to a list and manipulate it, just do

outputs = fake_stdout.getvalue().strip().split()
print(outputs)
# Outputs ['6', '9', '10', '10', '4', '9', '10', '8', '5', '3']

import collections
print(collections.Counter(outputs).most_common(1)[0][0])
# Outputs 10
enzo
  • 9,861
  • 3
  • 15
  • 38
  • Yes, and this is the answer I was hoping no one would write. You can do these things, but you have to be careful to restore `stdout` after, just as you have demonstrated. – quamrana Aug 14 '21 at 17:15
  • yea I tested your code and it worked like a charm... but it seems longish and @quamrana made a fair point too. I really appreciate your work though! – KnowsCount Aug 14 '21 at 17:18
  • @quamrana I've added a safer alternative, thanks for the hint! – enzo Aug 14 '21 at 17:20