35

Just as a dynamic class can be created using type(name, base-classes, namespace-dict), can a dynamic function be created?

I've tried doing something along the lines of:

>>> f = type("f", (function,), {})
NameError: name 'function' is not defined

Ok, so I'll be clever, but:

>>> def fn():
...   pass
... 
>>> type(fn)
<type 'function'>
>>> f = type("f", (type(fn),), {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: type 'function' is not an acceptable base type

Does Python specifically prevent the creation of dynamic functions in the same way it allows dynamic classes?

Edit: Note, I'd disallow any use of exec.. Since my question is does the Python language itself permit this.

Thanks in advance.

Bill
  • 2,319
  • 9
  • 29
  • 36
  • 3
    You can have lambda functions: `foo = lambda x: x*2`. And you can make any class callable, so... – Felix Kling Apr 24 '12 at 17:55
  • 1
    But I don't think a lambda in python is equivalent to the classical notion of a lambda as an anonymous function, right? A lambda in python is far more limited, since it can basically only handle a single expression, right? – Bill Apr 24 '12 at 17:58
  • 1
    but since a list comprehension is a single expression.... you can loop, assign, have side-effects, control flows, nest those ... – ch3ka Apr 24 '12 at 18:00
  • 5
    You're trying to subclass a function. `types.FunctionType` is a type that cannot be subclassed in Python. And what exactly do you mean by "dynamic" functions? – Zaur Nasibov Apr 24 '12 at 18:01
  • @FelixKling So let's say I just overwrite the _ _ call _ _ attribute of a dynamic class. Other than setting it to a already defined function (like fn in my example above), is there a way to dynamically construct the body of that function? I hope my question is clear, I'm still pondering the best way to put it. – Bill Apr 24 '12 at 18:06
  • It's definitely possible. Remember that a function in Python is essentially just an instance of a class with a __call__ method that runs the code defined. Sure you can dinamically generate code, but I wouldn't recommend it for serious uses. What you want to do seems to be achievable with other means. Check the partial module. – Pedro Werneck Apr 24 '12 at 18:14
  • The question is supposed to be fairly esoteric - I don't have a specific use case for this. Python is highly introspective, I just want to see how far that can be pushed. – Bill Apr 24 '12 at 18:19

8 Answers8

51

There is types.FunctionType which you can use to dynamically create a function e.g.

def test_func(): print 'wow' 
dynf = types.FunctionType(test_func.func_code, {})
dynf()

Output:

wow

You might object that this is not dynamic because I am using code from another function, but that was just an example there is a way to generate code from python strings e.g.

dynf = types.FunctionType(compile('print "really WoW"', 'dyn.py', 'exec'), {})
dynf()

Output:

really WoW

Now that is dynamic!

OP is worried about the dynamic nature of such function so here is another example

dynf = types.FunctionType(compile('test_func():\ntest_func()', 'dyn.py', 'exec'), globals())
dynf()

Output:

wow
wow

Note: Creating Function object like this seems to have limitations e.g. it is not easy to pass arguments, because to pass arguments we need to pass correct co_argcount, co_varnames and other 12 variables to types.CodeType, which theoretically can be done but will be error prone, an easier way is to import string as a module and you have a full fledged function e.g.

import types
import sys,imp

code = """def f(a,b,c):
    print a+b+c, "really WoW"
"""
module = imp.new_module('myfunctions')
exec code in module.__dict__
module.f('W', 'o', 'W')

Output:

WoW really WoW
Anurag Uniyal
  • 85,954
  • 40
  • 175
  • 219
  • Hm.. OK thanks. I think my question was a little poorly-defined. What if I now want dynf to call test_func twice? – Bill Apr 24 '12 at 18:11
  • @B.VB. I have showed you what you asked a true dynamic function and it will do anything you want as a normal function, try to do some changes. – Anurag Uniyal Apr 24 '12 at 18:19
  • 2
    Note that there doesn't appear to be a way to create a function that accepts any arguments in this manner; the compiled code object doesn't have the required co_argcount. – Martijn Pieters Jul 02 '12 at 11:39
  • 2
    @MartijnPieters added an example for a generic function – Anurag Uniyal Jul 02 '12 at 14:29
  • Why there is `module.__dict__` instead of `{}`? – Ecir Hana Jul 02 '12 at 15:21
  • @EcirHana so that all variables are inserted in module namespace – Anurag Uniyal Jul 02 '12 at 15:31
  • @AnuragUniyal: yes, but you immediately throw away the module - wouldn't it be simpler to just execute it in `{}`, instead of importing `imp` and creating new module? Is there something else a module provides for this situation? – Ecir Hana Jul 02 '12 at 15:35
  • @EcirHana No module doesn't provide anything special except for being a container and I can grab all such functions into such module and use it anywhere else by adding to `sys.modules`; alternative is `d = {} exec code in d d['f']('W', 'o', 'W')` which is almost same lines but less readable and can't be used anywhere else – Anurag Uniyal Jul 02 '12 at 16:29
  • This is the most verbose of the solutions posted here, for no very good reason. – Marcin Feb 01 '14 at 22:25
  • Is there a way to perform the last example, but without needing the `code` string? That is, to dynamically make a function on the fly, with arguments, output Python statements directly? I'd like to repeat that example, but never need any code embedded in string format. – ely Mar 20 '14 at 21:25
15

You'll want to look into collections.Callable, which is just a good place to start when defining __call__.

from collections import Callable
class SomeCallableClass(Callable):
    def __call__(self, x):
        print(x)

some_function = SomeCallableClass()
some_function(1)

Will give us 1 as out output. This allows you to construct functions at will.

from collections import Callable
class SomeCallableClass(Callable):
    def __init__(self, n):
        self.n = n
    def __call__(self, x):
        for i in range(self.n):
            print(x)

some_function = SomeCallableClass(2)
some_function("Two times.")
some_function = SomeCallableClass(3)
some_function("Three times.")

Which gives us:

Two times.
Two times.
Three times.
Three times.
Three times.

You can use this to construct functions as complex as you want.

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • This is getting interesting. It looks like this can be build up into a more general way of constructing functions. I need to think about this a bit more. Thanks. – Bill Apr 24 '12 at 18:24
  • It's not necessary to define ones own callable class to dynamically create functions; indeed in many ways that would be less dynamic. – Marcin Jan 31 '14 at 20:51
  • @Marcin Of course you don't *need* to, but in some cases it's a nice way to model it. The other opens are the many other answers. – Gareth Latty Feb 01 '14 at 21:47
14

You can avoid generating then execing source code if you're ready to generate Abstract Syntax Trees (AST's) and compile them instead. It might be slightly better because data can stay structured all along.

from ast import *
from types import *

function_ast = FunctionDef(
    name='f',
    args=arguments(args=[], vararg=None, kwarg=None, defaults=[]),
    body=[Return(value=Num(n=42, lineno=1, col_offset=0), lineno=1, col_offset=0)],
    decorator_list=[],
    lineno=1,
    col_offset=0
)
module_ast = Module(body=[function_ast])

module_code = compile(module_ast, "<not_a_file>", "exec")
function_code = [c for c in module_code.co_consts if isinstance(c, CodeType)][0]

f = FunctionType(function_code, {})

print f()

The code above will print 42.

To get inspiration about what the generated AST should be, you can use:

print(dump(parse("def f(): return 42"), include_attributes=True))

Of course, ASTs are different in Python 2 and Python 3.

Edit:

Tested and working in Python 3.8

from ast import *
from types import *

function_ast = FunctionDef(
    name='f',
    args=arguments(
        args=[], vararg=None, kwarg=None, defaults=[],
        kwonlyargs=[], kw_defaults=[], posonlyargs=[]
    ),
    body=[Return(value=Num(n=42, lineno=1, col_offset=0), lineno=1, col_offset=0)],
    decorator_list=[],
    lineno=1,
    col_offset=0
)
module_ast = Module(body=[function_ast], type_ignores=[])

module_code = compile(module_ast, "<not_a_file>", "exec")
function_code = [c for c in module_code.co_consts if isinstance(c, CodeType)][0]

f = FunctionType(function_code, {})

print(f())
paultop6
  • 3,691
  • 4
  • 29
  • 37
jacquev6
  • 620
  • 4
  • 18
  • Executing your code, I have this error : `TypeError: required field "kwonlyargs" missing from arguments` – Olórin Nov 04 '17 at 07:46
  • 1
    @ujsgeyrr1f0d0d0r0h1h0j0j_juj My answer is for Python 2 and works at least on 2.7.14. This error you're seeing happens on Python 3. Some work is required to adapt my answer to Python 3, as hinted by the last sentence. I encourage anyone doing this adaptation to edit my answer to show both cases. – jacquev6 May 01 '18 at 07:15
  • Fix for python 3 : args=arguments(args=[], vararg=None, kwarg=None, defaults=[], kwonlyargs=[], kw_defaults=[]), – philip Mar 09 '19 at 19:13
  • please please please don't use "from import *". please please please. It is appaling. – upgrd Apr 29 '23 at 15:21
4

A lot of people seem to be misled about the purpose of “lambda” in Python: its sole purpose is to define a simple single-expression function without a name. Nothing more. In Python, functions are indeed first-class objects, like they are in, say, LISP: you can pass them as arguments, store them in data structures, and return them as results. For example, here is a function that composes two given functions f and g, so compose(f, g)(x) is equivalent to f(g(x)):

def compose(f, g) :
    def result(x) :
        return f(g(x))
    #end result
    return result
#end compose

and here’s an example use:

>>> h = compose(lambda x : x + 2, lambda x : x * x)
>>> h(3)
11
Lawrence D'Oliveiro
  • 2,768
  • 1
  • 15
  • 13
4

Yes. Use the def statement:

def functor(f): # this dynamically creates a function at module load time
    def inner(g): #this dynamically creates a function on execution of functor
        return f(g)

    return inner

Any solution involving freestanding text being compiled is equivalent to exec or eval, which leaves you with pre-existing functions and lexically captured data items to stitch together new functions. This might give you some ideas.

Marcin
  • 48,559
  • 18
  • 128
  • 201
2

Python does allow creation of dynamic functions. One approach is using lambda:

>>> g = lambda x: x**2
>>> g
<function <lambda> at 0xa68c924>
>>> g(3)
9
>>> g = lambda x: x*2
>>> g
<function <lambda> at 0xa68c95c>
>>> g(3)
6
>>> 

Another approach is described here: Lexical closures in Python

So, you don't need the hocus-pocus of the behavioral patterns like Strategy.

It would be useful if you could tell us the problem you want to solve so we could find could out which language constructs are appropriate for that.

Community
  • 1
  • 1
Tamas Rev
  • 7,008
  • 5
  • 32
  • 49
0

jacquev6's solution works quite well for me after updating it for Python 3 by adding kwonlyargs=[], kw_defaults=[] to the call to arguments() :

#!/usr/bin/env python3

from ast import *
from types import *

function_ast = FunctionDef(
    name='f',
    args=arguments(args=[], vararg=None, kwarg=None, defaults=[], kwonlyargs=[], kw_defaults=[]),
    body=[Return(value=Num(n=42, lineno=1, col_offset=0), lineno=1, col_offset=0)],
    decorator_list=[],
    lineno=1,
    col_offset=0
)
module_ast = Module(body=[function_ast])
module_code = compile(module_ast, "<not_a_file>", "exec")
function_code = [c for c in module_code.co_consts if isinstance(c, CodeType)][0]

f = FunctionType(function_code, {})

print(f())
philip
  • 131
  • 2
  • 4
0

python provides limited support for anonymous executable objects, and that is lambda, though it does not support statements, only expressions.

This is due to the syntax of python, and there will be no simple workarounds.

If you need to generate more complex executable objects, then don't be anonymous, just create a function object in local scope with a non-conflicting name.

These are described in the documentation.

Python lambda expressions cannot contain statements because Python’s syntactic framework can’t handle statements nested inside expressions. However, in Python, this is not a serious problem. Unlike lambda forms in other languages, where they add functionality, Python lambdas are only a shorthand notation if you’re too lazy to define a function.

Functions are already first class objects in Python, and can be declared in a local scope. Therefore the only advantage of using a lambda instead of a locally-defined function is that you don’t need to invent a name for the function – but that’s just a local variable to which the function object (which is exactly the same type of object that a lambda expression yields) is assigned!

https://docs.python.org/3.7/faq/design.html#why-can-t-lambda-expressions-contain-statements

Andy
  • 1,077
  • 1
  • 8
  • 20