0

I am trying to run a simple loop in Python to avoid having to run the same code over and over but I keep getting an error. I think this is due to the fact that the loop runs in a function. To illustrate this let me show you when the code is not carried in the loop, repeatedly form:

def cosine_sim0(data0, data1):
    tfidf = vectorizer.fit_transform([data0, data1])
    return ((tfidf * tfidf.T).A)[0,1]
print(cosine_sim0(data0, data1))

def cosine_sim1(data1, data2):
    tfidf = vectorizer.fit_transform([data1, data2])
    return ((tfidf * tfidf.T).A)[0,1]
print(cosine_sim1(data1, data2))

def cosine_sim2(data2, data3):
    tfidf = vectorizer.fit_transform([data2, data3])
    return ((tfidf * tfidf.T).A)[0,1]
print(cosine_sim2(data2, data3))

def cosine_sim3(data3, data4):
    tfidf = vectorizer.fit_transform([data3, data4])
    return ((tfidf * tfidf.T).A)[0,1]
print(cosine_sim3(data3, data4)) 

As it can be seen, the loop should create 4 separate functions: cosine_sim%d %i, it should also add a number to the one starting from in the function and in the printing result. Having these into account I attempt by building the loop using the following code:

my_funcs = {}
    for i in range(4):
        def foo(data%d %i, data%d+1 %i):
                tfidf = vectorizer.fit_transform([data%d %i, data%d+1 %i])
                return ((tfidf * tfidf.T).A)[0,1]


        foo.func_name = "cosine_sim%d" % i
        my_funcs["cosine_sim%d" % i] = foo

    globals().update(my_funcs) # Export to namespace

    cosine_sim2(data1, data2)

As most of you can probably guess, the error retrieved states invalid syntax. Any suggestions of where the problem lies?

kind regards

Economist_Ayahuasca
  • 1,648
  • 24
  • 33
  • That's a funky way to approach meta-programming! :) It's often done via decorators and/or class inheritance instead... More on that [here](https://stackoverflow.com/questions/2789460/python-add-to-a-function-dynamically). Anyway, your indentation is off in the last code snippet (the `for` loop). `tfidf = ...` and `return ...` should be indented by 4 spaces – jDo Apr 04 '16 at 11:08
  • Thanks for your answer @jDo , what do you mean by intended by 4 spaces? cheers, – Economist_Ayahuasca Apr 04 '16 at 11:12
  • [Like this](http://pastebin.com/raw/M4fLtXR5). You don't *have* to use 4 spaces as long as the number of spaces used to indicate nesting is consistent (nesting = placing something within the scope of something else; think curly braces in C, java, etc.). 4 spaces is just the PEP8 style guide convention that the python community seems to have agreed upon. – jDo Apr 04 '16 at 11:14
  • 1
    A line of code that is meant to be inside of another block should have four more spaces of indentation than the start of the block. In this case, `tfidf = ...` and `return ...` are not part of the function since they aren't indented enough. – zondo Apr 04 '16 at 11:15
  • @zondo I updated the question with the extra spaces in these two lines but I still get the same error. – Economist_Ayahuasca Apr 04 '16 at 11:25
  • 1
    Take a look at the last function (in the bottom block). The `def ...` line is at the same indentation as the lines below. You need to indent by four more spaces whatever is supposed to be inside of the function. – zondo Apr 04 '16 at 11:29
  • @zondo Sorry I thought I updated it, now the code should reflect your idea. But still gives me a syntax error... – Economist_Ayahuasca Apr 04 '16 at 11:36
  • @AndresAzqueta Is the syntax error caused by the same lines as before (the `for` loop)? If so, try replacing the lines in your script with the code from my pastebin link. Otherwise, post the problematic code and traceback here. – jDo Apr 04 '16 at 11:45

1 Answers1

2

You can create the function in cycle (if properly indented and you can't use the % character in the name), then you can change it's name by accessing the func_name attribute, but that's perhaps not necessary. You can store the functions as values in a dict with their name as key. If you really want to have the function in the namespace and not dict, you can do globals()['my_func_name'] = my_func_object

Update: Example code

my_funcs = {}

for i in range(4):
    def foo():
        pass
        # Do stuff here that depends on i
    foo.func_name = "fancy_name_%d" % i # This is probably useless and can be omitted
    my_funcs["fancy_name_%d" % i] = foo

# Now you can do.
my_funcs['fancy_name_1']()

globals().update(my_funcs) # Export to namespace
# Now you can also do
fancy_name_1()
user1747134
  • 2,374
  • 1
  • 19
  • 26
  • Hi @user1747134 , thanks very much for the answer. Would it be possible for you to accommodate your idea in a code so it would be easier for me to picture it? – Economist_Ayahuasca Apr 04 '16 at 11:32
  • thanks very much for your answer again. I updated the code with your approach but still gives me error... could you check if it is something that I am doing wrong at first sight? Kind regards – Economist_Ayahuasca Apr 04 '16 at 15:05
  • 1
    You can't do things like `[data%d %i, data%d+1 %i]`. The % is the modulo operator and it's overloaded for strings to do formatting. It's not a syntactic construct that could name variables like this. You probably want to have a list `data` and do `[data[i], data[i+1]]` instead. If you really want to work with them as separate variables (I see no good reason, though), you can do it the same way as in the example for functions. – user1747134 Apr 05 '16 at 09:22