0

I am trying to implement a function (make_q) that returns a list of functions(Q) that are generated using the argument that make_q gets (P). Q is a variable dependent to n(=len(P)) and making the Q functions are similar, so it can be done in a for loop but here is the catch if I name the function in the loop, they will all have the same address so I only get the last Q, Is there to bypass this? Here is my code,

 def make_q(self):
        Temp_P=[p for p in self.P]
        Q=()
        for i in range(self.n-1):
            p=min(Temp_P)
            q=max(Temp_P)
            index_p=Temp_P.index(p)
            index_q=Temp_P.index(q)
            
            def tempQ():
                condition=random.random()
                if condition<=(p*self.n):
                    return index_p
                else:
                    return index_q
            Temp_Q=list(Q)
            Temp_Q.append(tempQ)
            Q=tuple(Temp_Q)
            q-=(1-p*self.n)/self.n
            Temp_P[index_q]=q
            Temp_P.pop(index_p)

        return Q
test.Q

(<function __main__.Test.make_q.<locals>.tempQ()>,
 <function __main__.Test.make_q.<locals>.tempQ()>,
 <function __main__.Test.make_q.<locals>.tempQ()>,
 <function __main__.Test.make_q.<locals>.tempQ()>,
 <function __main__.Test.make_q.<locals>.tempQ()>)

I also tried to make them a tuple so they have different addresses but it didn't work. Is there a way to name functions(tempQ) dynamic like tempQi

5
  • You can use metaclass to generate dynamic functions with variable names Commented Jul 23, 2020 at 14:42
  • @vestronge I have never heard of metaclasses can you explain more? Commented Jul 23, 2020 at 14:57
  • 1
    There's no issue with the functions all having the same name, and they don't actually have the same address. The problem is that they're all accessing the same values of index_p and index_q, which will be those of the final loop iteration in make_q(). You need to capture those values as of the time the function was defined: one way is to pass them as default parameters to the generated function, which in Python are evaluated at defintion time: def tempQ(index_p=index_p, index_q=index_q):. Commented Jul 23, 2020 at 14:57
  • Give me some time. Let me add some explanation and a solution Commented Jul 23, 2020 at 15:14
  • @jasonharper that actually did it! thanks Commented Jul 23, 2020 at 15:19

2 Answers 2

1

jasonharper's observation and solution in comments is correct(and should be the accepted answer). But since you asked about metaclasses, I am posting this anyway.

In python, each class is a type , with "name", "bases" (base classes) and "attrs"(all members of a class). Essentially, a metaclass defines a behaviour of a class, you can read more about it at https://www.python-course.eu/python3_metaclasses.php and various other online tutorials.

The __new__ method runs when a class is set up. Note the usage of attrs where your class member self.n is accessed by attrs['n'] (as attrs is a dict of all class members). I am defining functions tempQ_0, tempQ_1... dynamically. As you can see, we can also add docstrings to this dynamically defined class members.

import random


class MyMetaClass(type):

    def __new__(cls, name, bases, attrs):
        Temp_P = [p for p in attrs['P']]
        for i in range(attrs['n'] - 1):
            p = min(Temp_P)
            q = max(Temp_P)
            index_p = Temp_P.index(p)
            index_q = Temp_P.index(q)

            def fget(self, index_p=index_p, index_q=index_q):  # this is an unbound method
                condition = random.random()
                return index_p if condition <= (p * self.n) else index_q

            attrs['tempQ_{}'.format(i)] = property(fget, doc="""
            This function returns {} or {} randomly""".format(index_p, index_q))

            q -= (1 - p * attrs['n']) / attrs['n']
            Temp_P[index_q] = q
            Temp_P.pop(index_p)

        return super(MyMetaClass, cls).__new__(cls, name, bases, attrs)


# PY2
# class MyClass(object):
#     __metaclass__ = MyMetaClass
#     n = 3
#     P = [3, 6, 8]


# PY3
class MyClass(metaclass=MyMetaClass):
    n = 3
    P = [3, 6, 8]

# or use with_metaclass from future.utils for both Py2 and Py3

# print(dir(MyClass))
print(MyClass.tempQ_0, MyClass.tempQ_1)

output

<property object at 0x10e5fbd18> <property object at 0x10eaad0e8>

So your list of functions is [MyClass.tempQ_0, MyClass.tempQ_1]

Sign up to request clarification or add additional context in comments.

Comments

0

Please try via formatted strings, for eg: "function_{}.format(name)" also, how do you want your output to look like?

1 Comment

I just need the output to be an irritable array of functions

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.