Подтвердить что ты не робот

В 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 разрешает это.

Спасибо заранее.

4b9b3361

Ответ 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.

Было бы полезно, если бы вы могли рассказать нам о проблеме, которую вы хотите решить, чтобы мы могли найти, какие языковые конструкции подходят для этого.