-5

Problem statement:

I am trying to read lines from my data and and output both the forward and reverse orientation by passing my list to a function. To solve what I am trying to do, I have to pipe the function-name as string. I am making a mock test below that replicates my original problem in a simple way.

my_str = 'abcdef\nijklmn\nstuv\n'
my_str = my_str.rstrip('\n').split('\n')

for lines in my_str:
    print(lines)
    line_list = list(lines)

    # I want to read both forward and reverse orientation using a function "(def orient_my_str():"
    # the reason to pass this into another function is because I have lots of data crunching to do in each orientation (not shown here).
    # but, below process typically resolves what I am trying to achieve

    line_rev = orient_my_str(line_list, orientation='reversed')
    line_fwd = orient_my_str(line_list, orientation='')

    print('list in reverse orientation :', line_rev)
    print('list in forward orientation :', line_fwd)
    print()


# I am only want to make one function not two, because if I make two functions ..
# .. I will have to copy a large amount of code below the for-loop.
# there should be a way to fix this problem (calling function using string name when required and not).
def orient_my_str(line, orientation):
    my_output = ''
    for item in eval(orientation)(line):
        my_output += item

    print(my_output)
    return my_output

# But, this only works for reverse orientation. I know the issue is with passing "eval('')(line)" but was hoping it would work.

I tried to fix my code using ideas from these links,

Use a string to call function in Python

Calling a function of a module by using its name (a string)

python function call with variable

but I cannot seem to work it out.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
everestial007
  • 6,665
  • 7
  • 32
  • 72
  • I think you didn't read my question carefully. To do what you are imply, I will either have to make two functions; which is not what I want - because I will have to copy a big chunk of code in my original program. I am sure sure there is way to fix this. – everestial007 Mar 13 '18 at 19:04
  • You can use this: `for item in eval(orientation + "(line)"):` – Pangeran Bottor Mar 13 '18 at 19:06
  • @PangeranBottor I cannot seem to understand what are you doing with the code you just typed in. How does this help ? – everestial007 Mar 13 '18 at 19:09
  • @PangeranBottor : That seems to work. I have never seen a code like this. Thanks much. – everestial007 Mar 13 '18 at 19:10
  • @PangeranBottor "seems to work" yes - good : no – Patrick Artner Mar 13 '18 at 19:15
  • 1
    @everestial007 @PatrickArtner Yeah, it's a little bit hacky that's why I wrote it as comment. The thing you should understand is `eval` evaluates passed string. Since what you need is either `reversed(line)` or just `line`, I wrap `line` with parantheses hence the code. – Pangeran Bottor Mar 13 '18 at 19:18
  • 1
    @everestial007 Regardless of how you feel your question is being received. It is not helpful to clutter said question with complaints about downvotes, etc. If you have issue with how people are reacting to your questions, look into posting on meta. – user3483203 Mar 13 '18 at 19:22
  • @chrisz : Why would you assume I have not read meta, mcve. By, what standard are simple question like this downvote? How many people really read the question throughly. Should I ask a very simple question like `how to use for loop in python`. This site is awesome, but I have recently found people just downvote, as if it is a video game. And, when it's downvoted the question just stops receiving attention. The votes aren't reverted even if the details are improved or exlained further in comment section. The only solution is to delete and put a new question - then admin has a problem with it. – everestial007 Mar 13 '18 at 19:26
  • @PangeranBottor : I tried to take your hack method and apply it to my original problem. But, I am having issues. I tried `for n in eval(orientation+range(len(v1[SOI + '_PI']))):` where `SOI` is just an input argument variable. How would you fix this ? – everestial007 Mar 13 '18 at 19:42
  • I cannot put `range(len(v1[SOI + '_PI']))` inside `""`, because that would disrupt the argument variable. – everestial007 Mar 13 '18 at 19:43
  • @everestial007 I'm afraid it would make things complicated :) Have you tried to declare a variable for your `len(v1[SOI + '_PI'])` and later use it on `eval`? But again, it requires string `.format` which is not good – Pangeran Bottor Mar 13 '18 at 19:53
  • 1
    I didn't downvote your question, but there's two things that are bad about it. First thing is that it is a so-called "XY question", instead of describing the goal, you ask how to achieve a certain (flawed!) solution. The other thing is that it violates the MCVE spirit in that the first two lines and the loop that follow could be replaced with just the loop's body. – Ulrich Eckhardt Mar 13 '18 at 20:03
  • 1
    BTW: It's also unclear why you can not pass the reversed sequence to the function like `rev = orient_my_str(reversed(line_list))`. Further, naming isn't your strong side either, because `line_list` is not a list of lines and `my_str` is not a string. These things matter! – Ulrich Eckhardt Mar 13 '18 at 20:11

2 Answers2

2

Dont use eval, keep your methods simple. You do not need to copy anything below the for:

def orient_my_str(line, reverse = False): 
    # add any needed preprocessing here, store result as new list
    # and use it in the following line instead of the variable line
    data = reversed(line) if reverse else line 

    my_output = ''
    for item in data: # operate on line or reversed line as needed
        my_output += item

    print(my_output)
    return my_output

line_list= ["1","2","3","4"]
line_rev = orient_my_str(line_list, reverse = True)
line_fwd = orient_my_str(line_list)

print(line_rev)
print(line_fwd)

Output:

4321
1234
4321
1234
Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • Thanks Patrick. Let me ask you. Did you had problem reading my question and problem? Why are people so whimsical about downvoting a simple, comprehensive question like this? – everestial007 Mar 13 '18 at 19:17
  • `data = reversed(line) if reverse else line ` seems to be the problem because in my original program I have the break the `line` first to do my data computation. I am not just writing the `line` back but the `manipulated line`. This was just a mock test file. – everestial007 Mar 13 '18 at 19:20
  • 3
    @everestial007 I havent had any problems reading your questions and did not downvote until you put this **big black text** on top of it. This method does the same as yours without use of eval which can be considered unsafe and passing strings that then work as functionname for something that can easily be solved otherwise.If you need to preprocess your list - do so, then reverse it when ever you are finished preprocessing it and do your big `for-loop` over the reversed partof it. – Patrick Artner Mar 13 '18 at 19:20
  • `data = reversed(line) if reverse else line` just escapes the `for loop`. So, it's not applicable. – everestial007 Mar 13 '18 at 19:29
  • 1
    @everestial007 I am not sure I get your last comment. It is a ternary operator that provides `line` either as is or reversed by the name `data` to your for loop. If you need further preprocessing on line, you can do that before it - I marked it as comment in the code. Your loop operates on `data` which is either reversed or not - wich is exactly the same as your "eval" solution would do. I can see you are not pleased with my solution, downvote it if you really think it does not solve your problem. – Patrick Artner Mar 13 '18 at 19:59
2

Here's the approach as already suggested in the comment:

def orient_my_str(line, preprocessor):
    my_output = ''
    for item in preprocessor(line):
        my_output += item

    print(my_output)
    return my_output

As I mentioned, you can pass a function as parameter. In order to call it, you do this:

line_rev = orient_my_str(line_list, preprocessor=reversed)
line_fwd = orient_my_str(line_list, preprocessor=lambda x: x)

You could also use a default parameter for preprocessor, if you don't like passing the lambda function explicitly.

In summary, the important point is that there's no need to pass the name of a function around in order to then look up that function and call it. Just pass the function itself as parameter.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
  • This seems to work pretty neat. It's nice to know that we can do `preprocessor=reversed` on a function directly rather than string typing them. It's astonishing how `lambda x: x` fit in there; and how would you default it ? – everestial007 Mar 13 '18 at 20:38
  • 1
    `preprocessor=iter` would also work nicely for the forward case – Mark Dickinson Mar 13 '18 at 21:30