정규식을 기반으로 호출 할 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) )
이것은 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
실제 문제를 해결하는 더 좋은 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의 깨끗한 접근 방식을 계속 합니다.
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]
EDITED with print(try to look at the pprint module) and control-flow:
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 really, really doesn't want to do this. Not only does it not have any way to define a multi-line anonymous function, but function definitions don't return the function, so even if this were syntactically valid...
mylist.sort(key=def _(v):
try:
return -v
except:
return None)
... it still wouldn't work. (Although I guess if it were syntactically valid, they'd make function definitions return the function, so it would work.)
So you can write your own function to make a function from a string (using exec
of course) and pass in a triply-quoted string. It's kinda ugly syntactically, but it works:
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.__name__ = "<anonymous>"
cache[text] = func
return func
# never executed; forces func to be local (a tiny bit more speed)
func = None
Usage:
mylist.sort(key=function("""(v):
try:
return -v
except:
return None"""))
The only way to make an anonymous function is with lambda
, and as you know, they can only contain a single expression.
You can create a number of functions with the same name so at least you don't have to think of new names for each one.
It would be great to have truly anonymous functions, but Python's syntax can't support them easily.
Personally I'd just name it something use it and not fret about it "hanging around". The only thing you'll gain by using suggestions such as as redefining it later or using del
to drop the name out of the namespace is a potential for confusion or bugs if someone later comes along and moves some code around without groking what you're doing.
You could use 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')
The only option is to use a lambda expression, like you mention. Without that, it is not possible. That is the way python works.
If your function is complicated enough to not fit in a lambda
function, then, for readability's sake, it would probably be best to define it in a normal block anyway.
ReferenceURL : https://stackoverflow.com/questions/6629876/choose-python-function-to-call-based-on-a-regex
'program story' 카테고리의 다른 글
JQuery 정렬 가능한 '변경'이벤트 요소 위치 (0) | 2021.01.11 |
---|---|
JSTL에서 지정된 횟수만큼 반복하는 방법은 무엇입니까? (0) | 2021.01.11 |
네임 스페이스 시스템에 'Forms'가 없습니다. (0) | 2021.01.11 |
벡터의 특정 위치에 요소 삽입 (0) | 2021.01.11 |
jQuery를 사용하여 전체 페이지의 HTML을 어떻게 얻습니까? (0) | 2021.01.10 |