2

I think this is basic and I've seen multiple versions of the question I am asking although they are all not exactly what I am facing.

I have a script with two functions currently, and I want the ability to evoke just the second function but it needs to run the first function. My script is still messy so I am using an example:

def func1(input):    # Does something with input data
    my_list = []
    for i in input:
        my_list.append(i)
    return my_list  # Returns a list

func1_list = func1(x) # Save the result from func1 into a variable

def func2(func1_list):   
    my_final_list = []       # This function just edits the first list          
    for val in my_list:       # Results from func2 are needed later on         
        temp = val**3
        my_final_list.append(temp)
    return my_final_list

Is there a way to execute the second function without having to call the first function when importing a script? I have no clue where to input the func1_list variable in func2 so that anyone can just go straight to func2 and run it (and it will automatically execute func1 anyways).

EDIT: Both func1 and func2 should be able to be executed individually - func2 relies on func1. Both will show information (func2 just showing modified information). Example: func1 will run some input and show the raw result, whereas func2 will take that raw result and modify it. I'd want the ability to see the raw and modified result if needed.

CuriousDude
  • 1,087
  • 1
  • 8
  • 21
  • 1
    Remove `func1_list = func1(x)` from the module scope of your code, then you can import without having `func1` called. – Moses Koledoye Jul 20 '17 at 18:22
  • Write a wrapper that performs the `func1_list = func1(x)` before invoking `func2`? – a_guest Jul 20 '17 at 18:22
  • Functions are closures so you could simply remove the `func1_list` parameter from the function (and change `for val in my_list` to `for val in func1_list`, or even better: `return [val**3 for val in func1_list]`. Alternatively you can make `func2` call `func1`. – Felix Kling Jul 20 '17 at 18:22
  • 2
    I think you need to step back a bit and explain what you're really trying to do. The question as is doesn't really make a lot of sense, and it's difficult to understand what fundamental problem you're trying to solve. – OldGeeksGuide Jul 20 '17 at 18:23
  • Pretty sure that if you ever end up calling `func2`, `my_list` will give a `NameError` – juanpa.arrivillaga Jul 20 '17 at 18:24
  • Thank you everyone for the quick responses! I did try inputting my variables calling func1 within the definition of func2 and that did cause a NameError. Still trying to figure out the kinks! – CuriousDude Jul 20 '17 at 18:29
  • I am getting confused now, in order to evoke either function 1 or 2 when importing the script, should the objects within the function definitions be the same? i.e. def func1(input) and def func2(input) ? – CuriousDude Jul 20 '17 at 18:37
  • Your question isn't completely clear. func2 relies on data that func1 generates. You want func2 to be easily runnable when importing the file this code is in. Do you expect func1 to be used directly by whatever imports this code, or is it just a convenience used by func2? Is func1 an expensive process, or is it cheap enough to call it every time func2 is run? When you say "results from func2 are needed later on", the same questions apply: is func2 cheap? will the importing code be expected to call func2, or is it a building block for some func3? Please edit your question with this info. – Scott Mermelstein Jul 20 '17 at 18:42
  • Both func1 and func2 should be able to be executed individually - it is just that func2 relies on func1. Both will show information (func2 just showing modified information). Example func1 will run input and show the raw result, whereas func2 will take that raw result and modify it. I'd want the ability to see the raw and modified result if needed. – CuriousDude Jul 20 '17 at 18:46

3 Answers3

0

If you want func1_list = func1(x) to only invoke when directly executing your script, you'll need to modify your script a bit so it is ignored when called from a module. You'll use an if condition like this:

if __name__ == '__main__':
    func1_list = func1(x) # Save the result from func1 into a variable

__name__ equals __main__ only when the script is invoked directly, so it will not execute when called from a module.


For a detailed description of how it works, look here.

cs95
  • 379,657
  • 97
  • 704
  • 746
  • Oh I have heard of this, never truly understood it. But what does it mean when you say it is invoked directly? I import my script so is that not it being called directly? I also just tried putting the variables within the function and that seems to work but I am not sure if that is best practise. – CuriousDude Jul 20 '17 at 18:26
  • If the file is called `A.py`, then running `python A.py` will cause the line to execute. But doing `import A` in `B.py` won't. – cs95 Jul 20 '17 at 18:29
  • @DNAngel Look at the official [docs](https://docs.python.org/3/library/__main__.html). It doesn't get any more official than that. – cs95 Jul 20 '17 at 18:30
  • Ohh okay I will have to play around this. I am getting confused on how to make my func2 use the same object in func1 so that it can work on the same dataset. i.e. def func2(input) – CuriousDude Jul 20 '17 at 18:35
  • @DNAngel Also, if any of the answers are helpful to you, you can upvote them. Check out https://stackoverflow.com/help/someone-answers . – Scott Mermelstein Jul 20 '17 at 19:07
0

If you are trying to import your script, you must follow the best practices and conventions.

Without starting to build a full-package, you need to, at least, ensure that you do not execute code that is not requested when importing the module.

Either you wrap your code into a class, or you remove function calls from within the root of your module and you provided an init_function() which must be called to prepare the environment.

There is no shame in using an init_function() and a lot of libraries do that.

However, I recommend that you structure your code within Python classes. Example of FunctionsManager.py:

class FunctionsManager:
    def __init__(self):  
        self.func1_list = self.func1(x) # Save the result from func1 into a variable

    def func1(self, input):    # Does something with input data
        my_list = []
        for i in input:
            my_list.append(i)
        return my_list  # Returns a list

    def func2(self, func1_list):   
        my_final_list = []       # This function just edits the first list          
        for val in my_list:       # Results from func2 are needed later on         
            temp = val**3
            my_final_list.append(temp)
        return my_final_list

And then in your main code:

from FunctionsManager import FunctionsManager

manager = FunctionsManager() # Calls __init__() and so func1()
result = manager.func2(yourlist)
Fabien
  • 4,862
  • 2
  • 19
  • 33
  • Thank you for the information. Classes always scared me (lol) but I do want to learn following best practises. I am a complete noob with classes but I will read up on it again and try to edit my functions accordingly! Nothing ventured, nothing gained! – CuriousDude Jul 20 '17 at 18:49
  • Yes, all my encouragements for you on this path :-) You will see it's easier than expected! Enjoy Python – Fabien Jul 20 '17 at 19:01
0

Note, your question is basically asking for a primer on dependency injection. You would probably do well to read up on it. It's language-agnostic - it applies as nicely to Java as it does to Python.

There are several different approaches you can take for this; the best choice depends on your needs and what the functions involved do (which is why I was asking all the questions in the comment).

The simplest form of what you're looking for is to just have one function invoke the other. (COLDSPEED made reference to this.):

def func1(input):
    # do some magic to my_list with input
    return my_list

def func2(input):
    func1_list = func1(input)
    # do some magic to make func2's my_list
    return my_list

In that case, func1 and func2 are both able to be called. The importing code doesn't have to worry about calling func1 before func2 - that's taken care of by func2.

Before I get into your other choices, you mentioned that both functions output information. It's a good practice to separate your calculations from your output of them. So, DON'T do this:

def func1(input):
    # do some magic to my_list with input
    print("func1: Stuff from my list...")
    return my_list

def func2(input):
    func1_list = func1(input)
    print("func2: Stuff from my list...")
    # do some magic to make func2's my_list
    return my_list

Because then calling func2 would print out the "func1" line and the "func2" line. Instead, separate the logic from the output. It may seem more complicated, but it gives you more building blocks to play with:

def func1(input):
    # do some magic to my_list with input
    return my_list

def func2(input):
    func1_list = func1(input)
    # do some magic to make func2's my_list
    return my_list

def output_func1(input):
    func1_list = func1(input)
    print("func1_list stuff")

def output_func2(input):
    func2_list = func2(input)
    print("func2_list stuff")

Now you have a lot of reusable functions that don't generate a lot of noise.

This is a good step, and you can use this easily. What happens if func1 takes an hour to run? You don't want to run it again if it's already been run. Then you want to use module variables to save state. Something like this:

func1_results = None

def func1(input):
    # do some magic to my_list with input
    func1_results = my_list
    return my_list

def func2(input):
    if not func1_results:
        func1(input) # this will only run if func1 hasn't been called yet
    # do some magic to make func2's my_list
    return my_list  # you could similarly make a construct to save these results   

Now you get code calling its dependencies only if it needs to. We're getting better. But we're passing input into func2, and we only care about it in func1. You could make a module variable input that func1 refers to, and your importing code could set it before calling any of the funcs:

input = None
def func1():
    # do some magic to my_list with module variable input

And invoke it as:

import my_funcs
my_funcs.input = "Happy happy input!"
my_funcs.func1() # or just my_funcs.func2(), etc.

This could be nice, but it's dangerous - what if the importing code doesn't set input? The solution to that is what Fabien mentioned about having a class:

class FuncRunner(object):
    def __init__(self, input):
        self.input = input
        self.func1_results = None
    def func1(self):
        # do some magic to local my_list with input
        self.func1_results = my_list
        return my_list
    def func2(self):
        if not self.func1_results:
            self.func1()
        # do some magic to make func2's my_list
        # maybe save the result as self.func2_results ?
        return my_list

This would be invoked as:

from my_funcs import FuncRunner
runner = FuncRunner("Happy happy input!")
foo = runner.func1()  # or foo = runner.func2(), as per your needs

This does the nice thing of not letting you run functions without constructing the object, and the way the __init__ is structured, you can't create the object without passing input. The results found by func1 are stored within the object, so they're always associated together. In your case, this is likely the cleanest way to do what you want.

Scott Mermelstein
  • 15,174
  • 4
  • 48
  • 76