最初に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) )
これは、 diのNice answer に基づいています。
無名関数を作成することの難しさは、ちょっとしたニシンだと思います。本当にやりたいことは、関連するコードをまとめて、コードをきれいにすることです。ですから、デコレータがあなたのために働くかもしれません。
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
より良いDRY実際の問題を解決する方法:
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,
]]
継続 Gareth's モジュール式の自己完結型ソリューションによるクリーンなアプローチ:
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)
名前空間をきれいに保ちたい場合は、delを使用します。
def myprint(msg):
print msg
f_list = [ myprint ]
del myprint
f_list[0]('hi')
あなたが述べたように、これはできません。ただし、近似することはできます。
def create_printer():
def myprint(x):
print x
return myprint
x = create_printer()
myprint
は、それが作成された変数スコープに呼び出し元がアクセスできないため、ここでは事実上匿名です。 ( Pythonのクロージャ を参照してください。)
名前空間の汚染が心配な場合は、別の関数内に関数を作成してください。次に、create_functions
関数のローカル名前空間のみを「汚染」し、外側の名前空間は汚染しません。
def create_functions():
def myprint(msg):
print msg
return [myprint]
f_list = create_functions()
f_list[0]('hi')
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.
ラムダが唯一の方法であると述べましたが、ラムダの制限についてではなく、それらを回避する方法を考える必要があります-たとえば、リスト、辞書、内包表記などを使用して、必要なことを行うことができます:
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]
Print( pprint module を見てみてください)および制御フローで編集:
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)
Pythonは本当にこれをしたくありません。複数行の匿名関数を定義する方法がないだけでなく、関数定義が関数を返さないため、構文的に有効であっても...
mylist.sort(key=def _(v):
try:
return -v
except:
return None)
...それでも動作しません。 (構文的に有効であるかどうかは推測しますが、関数定義が関数を返すようにするので、wouldは機能します。)
したがって、文字列から関数を作成する独自の関数を作成し(もちろんexec
を使用して)、三重引用符で囲まれた文字列を渡すことができます。それはちょっとい構文ですが、動作します:
def function(text, cache={}):
# strip everything before the first paren in case it's "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.__= "<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"""))
無名関数を作成する唯一の方法はlambda
を使用することであり、ご存じのように、それらは単一の式のみを含むことができます。
同じ名前の関数をいくつでも作成できるため、少なくともそれぞれの関数の新しい名前を考える必要はありません。
本当に匿名の関数があれば素晴らしいのですが、Pythonの構文はそれらを簡単にサポートできません。
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')
個人的には、使用するものに名前を付け、「ぶらぶら」することを心配しません。後で再定義したり、del
を使用して名前空間から名前を削除するなどの提案を使用することで得られる唯一のことは、後で誰かがやって来て、あなたがしていることを学んでいます。
あなたが言及したように、唯一のオプションはラムダ式を使用することです。それがなければ、それは不可能です。それがpythonの仕組みです。