В Python возможны реальные динамические и анонимные функции?
Как динамический класс может быть создан с использованием типа (name, base-classes, namespace-dict), может ли быть создана динамическая функция?
Я пробовал делать что-то по строкам:
>>> f = type("f", (function,), {})
NameError: name 'function' is not defined
Хорошо, поэтому я буду умным, но:
>>> 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
Является ли Python специально препятствием для создания динамических функций так же, как позволяет динамические классы?
Изменить: заметьте, я бы запретил любое использование exec. Поскольку мой вопрос заключается в том, что сам язык Python разрешает это.
Спасибо заранее.
Ответы
Ответ 1
Существует types.FunctionType
, который можно использовать для динамического создания функции, например.
def test_func(): print 'wow'
dynf = types.FunctionType(test_func.func_code, {})
dynf()
Вывод:
wow
Вы можете возразить, что это не динамическое, потому что я использую код из другой функции, но это был всего лишь пример, чтобы создать код из строк python, например.
dynf = types.FunctionType(compile('print "really WoW"', 'dyn.py', 'exec'), {})
dynf()
Вывод:
really WoW
Теперь это динамично!
OP беспокоится о динамическом характере такой функции, так что вот еще один пример
dynf = types.FunctionType(compile('test_func():\ntest_func()', 'dyn.py', 'exec'), globals())
dynf()
Вывод:
wow
wow
Примечание:
Создание объекта функции, подобного этому, кажется, имеет ограничения, например. непросто передать аргументы, потому что для передачи аргументов нам нужно передать правильные значения co_argcount, co_varnames и другие 12 переменных в types.CodeType
, которые теоретически могут быть выполнены, но будут подвержены ошибкам, проще всего импортировать строку в качестве модуля и у вас есть полноценная функция, например
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')
Вывод:
WoW really WoW
Ответ 2
Вам нужно посмотреть collections.Callable
, что является хорошим местом для начала при определении __call__
.
from collections import Callable
class SomeCallableClass(Callable):
def __call__(self, x):
print(x)
some_function = SomeCallableClass()
some_function(1)
Дает нам 1
как выход. Это позволяет вам строить функции по желанию.
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.")
Что дает нам:
Two times.
Two times.
Three times.
Three times.
Three times.
Вы можете использовать это для построения сложных функций, которые вам нужны.
Ответ 3
Многие люди, похоже, вводятся в заблуждение относительно цели "лямбда" в Python: его единственная цель - определить простую функцию одного выражения без имени. Больше ничего. В Python функции действительно являются первоклассными объектами, такими как они, скажем, LISP: вы можете передавать их как аргументы, хранить их в структурах данных и возвращать их в качестве результатов. Например, здесь есть функция, которая состоит из двух заданных функций f и g, поэтому compose (f, g) (x) эквивалентно f (g (x)):
def compose(f, g) :
def result(x) :
return f(g(x))
#end result
return result
#end compose
и heres пример использования:
>>> h = compose(lambda x : x + 2, lambda x : x * x)
>>> h(3)
11
Ответ 4
Вы можете избежать генерации исходного кода exec
, если вы готовы генерировать АСТ и скомпилировать их. Это может быть немного лучше, потому что данные могут оставаться структурированными все время.
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()
В приведенном выше коде будет напечатан 42
.
Чтобы получить вдохновение в отношении того, что должен был сформировать AST, вы можете использовать:
print(dump(parse("def f(): return 42"), include_attributes=True))
Конечно, АСТ отличаются в Python 2 и Python 3.
Ответ 5
Да. Используйте оператор def
:
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
Любое решение с использованием компилируемого автономного текста эквивалентно exec
или eval
, что оставляет вас с уже существующими функциями и лексически захваченными элементами данных, чтобы сшить новые функции. Это может дать вам некоторые идеи.
Ответ 6
Python позволяет создавать динамические функции. Один подход заключается в использовании лямбда:
>>> 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
>>>
Ниже описан другой подход: Лексические замыкания в Python
Итак, вам не нужен фокус-фокус поведенческих паттернов, например Strategy.
Было бы полезно, если бы вы могли рассказать нам о проблеме, которую вы хотите решить, чтобы мы могли найти, какие языковые конструкции подходят для этого.