Search code examples
pythonpython-3.xdynamic-code

why i face problem when i'm going to pass a dynamic function in Python


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()

Solution

  • 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.