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

Как сделать анонимную функцию в Python без ее крещения?

Можно ли поместить функцию в структуру данных, не указав сначала имя с def?

# This is the behaviour I want. Prints "hi".
def myprint(msg):
    print msg
f_list = [ myprint ]
f_list[0]('hi')
# The word "myprint" is never used again. Why litter the namespace with it?

Тело лямбда-функции сильно ограничено, поэтому я не могу их использовать.

Изменить: для справки, это больше похоже на реальный код, в котором я столкнулся с проблемой.

def handle_message( msg ):
    print msg
def handle_warning( msg ):
    global num_warnings, num_fatals
    num_warnings += 1
    if ( is_fatal( msg ) ):
        num_fatals += 1
handlers = (
    ( re.compile( '^<\w+> (.*)' ), handle_message ),
    ( re.compile( '^\*{3} (.*)' ), handle_warning ),
)
# There are really 10 or so handlers, of similar length.
# The regexps are uncomfortably separated from the handler bodies,
# and the code is unnecessarily long.

for line in open( "log" ):
    for ( regex, handler ) in handlers:
        m = regex.search( line )
        if ( m ): handler( m.group(1) )
4b9b3361

Ответ 1

Это основано на удачном ответе Udi.

Я думаю, что сложность создания анонимных функций - это немного красная селедка. То, что вы действительно хотите сделать, это сохранить связанный код вместе и сделать код аккуратным. Поэтому я думаю, что декораторы могут работать на вас.

import re

# List of pairs (regexp, handler)
handlers = []

def handler_for(regexp):
    """Declare a function as handler for a regular expression."""
    def gethandler(f):
        handlers.append((re.compile(regexp), f))
        return f
    return gethandler

@handler_for(r'^<\w+> (.*)')
def handle_message(msg):
    print msg

@handler_for(r'^\*{3} (.*)')
def handle_warning(msg):
    global num_warnings, num_fatals
    num_warnings += 1
    if is_fatal(msg):
        num_fatals += 1

Ответ 2

Более суровый способ решить вашу актуальную проблему:

def message(msg):
    print msg
message.re = '^<\w+> (.*)'

def warning(msg):
    global num_warnings, num_fatals
    num_warnings += 1
    if ( is_fatal( msg ) ):
        num_fatals += 1
warning.re = '^\*{3} (.*)'

handlers = [(re.compile(x.re), x) for x in [
        message,
        warning,
        foo,
        bar,
        baz,
    ]]

Ответ 3

Постоянный чистый подход Гарета с модульным автономным решением:

import re

# in util.py
class GenericLogProcessor(object):

    def __init__(self):
      self.handlers = [] # List of pairs (regexp, handler)

    def register(self, regexp):
        """Declare a function as handler for a regular expression."""
        def gethandler(f):
            self.handlers.append((re.compile(regexp), f))
            return f
        return gethandler

    def process(self, file):
        """Process a file line by line and execute all handlers by registered regular expressions"""
        for line in file:
            for regex, handler in self.handlers:
                m = regex.search(line)
                if (m):
                  handler(m.group(1))      

# in log_processor.py
log_processor = GenericLogProcessor()

@log_processor.register(r'^<\w+> (.*)')
def handle_message(msg):
    print msg

@log_processor.register(r'^\*{3} (.*)')
def handle_warning(msg):
    global num_warnings, num_fatals
    num_warnings += 1
    if is_fatal(msg):
        num_fatals += 1

# in your code
with open("1.log") as f:
  log_processor.process(f)

Ответ 4

Если вы хотите сохранить чистое пространство имен, используйте del:

def myprint(msg):
    print msg
f_list = [ myprint ]
del myprint
f_list[0]('hi')

Ответ 5

Как вы сказали, это невозможно. Но вы можете его аппроксимировать.

def create_printer():
  def myprint(x):
    print x
  return myprint

x = create_printer()

myprint является фактически анонимным здесь, поскольку область переменных, в которой она была создана, больше не доступна для вызывающего. (См. закрытия в Python.)

Ответ 6

Если вы обеспокоены загрязнением пространства имен, создайте свои функции внутри другой функции. Затем вы только "загрязняете" локальное пространство имен функции create_functions, а не внешнее пространство имен.

def create_functions():
    def myprint(msg):
        print msg
    return [myprint]

f_list = create_functions()
f_list[0]('hi')

Ответ 7

Вы не должны этого делать, потому что eval - это зло, но вы можете скомпилировать код функции во время выполнения, используя FunctionType и compile:

>>> def f(msg): print msg
>>> type(f)
 <type 'function'>
>>> help(type(f))
...
class function(object)
 |  function(code, globals[, name[, argdefs[, closure]]])
 |
 |  Create a function object from a code object and a dictionary.
 |  The optional name string overrides the name from the code object.
 |  The optional argdefs tuple specifies the default argument values.
 |  The optional closure tuple supplies the bindings for free variables.    
...

>>> help(compile)
Help on built-in function compile in module __builtin__:

compile(...)
    compile(source, filename, mode[, flags[, dont_inherit]]) -> code object

    Compile the source string (a Python module, statement or expression)
    into a code object that can be executed by the exec statement or eval().
    The filename will be used for run-time error messages.
    The mode must be 'exec' to compile a module, 'single' to compile a
    single (interactive) statement, or 'eval' to compile an expression.
    The flags argument, if present, controls which future statements influence
    the compilation of the code.
    The dont_inherit argument, if non-zero, stops the compilation inheriting
    the effects of any future statements in effect in the code calling
    compile; if absent or zero these statements do influence the compilation,
    in addition to any features explicitly specified.

Ответ 8

Поскольку все указанные лямбда - единственный способ, но вы должны думать не о лямбда-ограничениях, а о том, как их избежать - например, вы можете использовать списки, дикты, понимание и т.д., чтобы делать то, что вы хотите:

funcs = [lambda x,y: x+y, lambda x,y: x-y, lambda x,y: x*y, lambda x: x]
funcs[0](1,2)
>>> 3
funcs[1](funcs[0](1,2),funcs[0](2,2))
>>> -1
[func(x,y) for x,y in zip(xrange(10),xrange(10,20)) for func in funcs]

ИЗМЕНИТЬ печать (попробуйте посмотреть модуль pprint) и поток управления:

add = True
(funcs[0] if add else funcs[1])(1,2)
>>> 3

from pprint import pprint
printMsg = lambda isWarning, msg: pprint('WARNING: ' + msg) if isWarning else pprint('MSG:' + msg)

Ответ 9

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

mylist.sort(key=def _(v):
                    try:
                        return -v
                    except:
                        return None)

... он все равно не сработает. (Хотя я предполагаю, что если бы это было синтаксически корректно, они бы определяли функции, возвращающие функцию, поэтому они будут работать.)

Итак, вы можете написать свою собственную функцию, чтобы сделать функцию из строки (используя, конечно, exec), и передать в трехстрочную строку. Это довольно уродливо синтаксически, но оно работает:

def function(text, cache={}):

    # strip everything before the first paren in case it "def foo(...):"
    if not text.startswith("("):
        text = text[text.index("("):]

    # keep a cache so we don't recompile the same func twice
    if text in cache:
        return cache[text]

    exec "def func" + text
    func.__name__ = "<anonymous>"

    cache[text] = func
    return func

    # never executed; forces func to be local (a tiny bit more speed)
    func = None

Использование:

mylist.sort(key=function("""(v):
                                try:
                                    return -v
                                except:
                                    return None"""))

Ответ 10

Единственный способ сделать анонимную функцию - lambda, и, как вы знаете, они могут содержать только одно выражение.

Вы можете создать несколько функций с тем же именем, чтобы по крайней мере вам не приходилось думать о новых именах для каждого из них.

Было бы здорово иметь действительно анонимные функции, но синтаксис Python не может легко их поддерживать.

Ответ 11

Лично я бы назвал его чем-то, использующим его, и не беспокоиться об этом "висит". Единственное, что вы получите, используя такие предложения, как переопределение или позже, или с помощью del, чтобы отбросить имя из пространства имен, является потенциальным путаницей или ошибками, если кто-то позже приходит и перемещает некоторый код, не забирая то, что вы делаю.

Ответ 12

Вы можете использовать exec:

def define(arglist, body):
    g = {}
    exec("def anonfunc({0}):\n{1}".format(arglist,
                                     "\n".join("    {0}".format(line)
                                               for line in body.splitlines())), g)
    return g["anonfunc"]

f_list = [define("msg", "print(msg)")]
f_list[0]('hi')

Ответ 13

Единственный вариант - использовать лямбда-выражение, как вы упомянули. Без этого это невозможно. Так работает python.

Ответ 14

Если ваша функция достаточно сложна, чтобы не вписываться в функцию lambda, то для удобства чтения было бы лучше всего определить ее в нормальном блоке.