1

I'm going to pass a function dynamically to another class as shown below

    class simulator(object):
        def __init__(self, fn_):

            print(self.test(fn_))


        def test(self, fn):
            return  fn(self, 20)


    class t(object):

        s = 'def get_fitness(x, y):\n return x+y'

        exec(s)

        def fnGetFitness(self,genes):
            return get_fitness(genes, 10)

        simulator(fnGetFitness)



    t()

but i face error below:

    File "N:/Job/GA/mine/dyn.py", line 25, in fnGetFitness
          return get_fitness(genes, 10)

    NameError: name 'get_fitness' is not defined

i guess its something related to scopes but i can't handle it anyone on this?

EDIT :

this is a simpler code, showing the problem :

    class t(object):
        def __init__(self):
            exec('def get_fitness(x, y):\n return x+y')
            print(get_fitness(2,3))
    t()

1 Answers1

1

nothing to do with exec. What you're doing is equivalent (with safety removed) to:

class t(object):
    def get_fitness(x,y):
        return x+y

but your method definition is at class level, but not on the simulator class.

simulator(fnGetFitness) calls fnGetFitness out of t class context, so it doesn't know your new function.

That cannot work (also get_fitness should be decorated as @staticmethod because it doesn't have a self parameter)

What works is to define dynamically (or not) the function at global level so class can call it

s = 'def get_fitness(x, y):\n return x+y'
exec(s)

class t(object):
    def fnGetFitness(self,genes):
        return get_fitness(genes, 10)

    simulator(fnGetFitness)

t()

that fixed it, but honestly I'm puzzled about the purpose (already took me a while to figure out how to make something run from your code)

EDIT: a simpler and somehow different (and exec related) code has been posted in comments:

class t(object):
    def __init__(self):
        exec('def get_fitness(x, y):\n return x+y')
        print(get_fitness(2,3))
t()

this raises NameError: name 'get_fitness' is not defined

now this has to do with exec. When __init__ is parsed, get_fitness isn't known because the parser didn't see it as a local variable, even if at the time of execution, it is set in locals() dictionary by exec (related: why is 'ord' seen as an unassigned variable here?).

A workaround is to fetch the function in the local variables like this:

class t(object):
    def __init__(self):
        exec('def get_fitness(x, y):\n return x+y')
        print(locals()["get_fitness"](2,3))

t()

this works & prints 5.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • In fact I want to read a function from a file or let user write their code in a TextCtrl then my app is possible to run the code. I can't find a way to define passed-code as a global function :( – Maghsood Salimi Oct 21 '18 at 08:17
  • it's difficult to see the relations and what the use function can do in the object context with just `x+y`. With `x+y`, you can insert it anywhere, it'll work. If you want to use helper methods, I suggest that you use a different method, like child classes. – Jean-François Fabre Oct 21 '18 at 08:21
  • here is the simplified version of my problem: class t(object): def __init__(self): exec('def get_fitness(x, y):\n return x+y') print(get_fitness(2,3)) t() – Maghsood Salimi Oct 21 '18 at 08:33
  • the problem in this (simpler) code in comment, is that `get_fitness` isn't dynamically defined. `get_fitness` doesn't exist when the function is parsed. `exec` something doesn't work to define dynamically at this level – Jean-François Fabre Oct 21 '18 at 09:22
  • see my edit for explanation & solution :) maybe you should edit your question to put this snippet, and I'll edit my answer. – Jean-François Fabre Oct 21 '18 at 09:30