[python] f- 문자열 평가를 연기 / 연기하는 방법은 무엇입니까?

일부 파일을 생성하기 위해 템플릿 문자열을 사용하고 있으며 다음과 같이 이전 템플릿 코드를 줄이기 위해 이러한 목적으로 새 f- 문자열의 간결함을 좋아합니다.

template_a = "The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print (template_a.format(**locals()))

이제이 작업을 수행하여 변수를 직접 대체 할 수 있습니다.

names = ["foo", "bar"]
for name in names:
    print (f"The current name is {name}")

그러나 때로는 템플릿을 다른 곳에서 정의하는 것이 합리적입니다. 코드의 더 높은 위치에 있거나 파일 등에서 가져온 것입니다. 이는 템플릿이 서식 태그가 포함 된 정적 문자열임을 의미합니다. 인터프리터에게 문자열을 새로운 f- 문자열로 해석하도록 지시하기 위해 문자열에 어떤 일이 발생해야하지만 그런 것이 있는지 모르겠습니다.

문자열을 가져와 .format(**locals())호출을 사용하지 않도록 f- 문자열로 해석하는 방법이 있습니까?

이상적으로는 다음과 같이 코딩 할 수 있기를 원합니다 … ( magic_fstring_function내가 이해하지 못하는 부분이 들어오는 곳) :

template_a = f"The current name is {name}"
# OR [Ideal2] template_a = magic_fstring_function(open('template.txt').read())
names = ["foo", "bar"]
for name in names:
    print (template_a)

…이 원하는 출력으로 (파일을 두 번 읽지 않음) :

The current name is foo
The current name is bar

…하지만 내가 얻는 실제 출력은 다음과 같습니다.

The current name is {name}
The current name is {name}



답변

여기에 완전한 “이상적인 2″가 있습니다.

f- 문자열이 아닙니다. f- 문자열도 사용하지 않지만 요청한대로 수행합니다. 지정된 구문과 정확히 일치합니다. 을 사용하지 않기 때문에 보안 문제가 없습니다 eval().

작은 클래스를 사용하고 __str__print에 의해 자동으로 호출되는 구현 합니다. 제한된 클래스 범위를 벗어나기 위해 inspect모듈을 사용하여 한 프레임 위로 이동하고 호출자가 액세스 할 수있는 변수를 확인합니다.

import inspect

class magic_fstring_function:
    def __init__(self, payload):
        self.payload = payload
    def __str__(self):
        vars = inspect.currentframe().f_back.f_globals.copy()
        vars.update(inspect.currentframe().f_back.f_locals)
        return self.payload.format(**vars)

template = "The current name is {name}"

template_a = magic_fstring_function(template)

# use it inside a function to demonstrate it gets the scoping right
def new_scope():
    names = ["foo", "bar"]
    for name in names:
        print(template_a)

new_scope()
# The current name is foo
# The current name is bar


답변

이는 템플릿이 서식 태그가 포함 된 정적 문자열임을 의미합니다.

예, 그렇기 때문에 교체 필드와 리터럴이있는 리터럴이 .format있으므로 원하는 때마다 필드를 호출 format하여 교체 할 수 있습니다 .

인터프리터에게 문자열을 새로운 f- 문자열로 해석하도록 지시하려면 문자열에 어떤 일이 발생해야합니다.

그것이 접두사 f/F입니다. 함수로 래핑하고 호출 시간 동안 평가를 연기 할 수 있지만 물론 추가 오버 헤드가 발생합니다.

template_a = lambda: f"The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print (template_a())

출력되는 내용 :

The current name is foo
The current name is bar

그러나 잘못된 느낌이 들며 대체 항목에서 전역 네임 스페이스 만 엿볼 수 있다는 사실로 제한됩니다. 로컬 이름이 필요한 상황에서 사용하려고 시도하면 문자열에 인수로 전달되지 않는 한 비참하게 실패합니다 (이는 완전히 요점을 능가합니다).

문자열을 가져와 .format(**locals())호출을 사용하지 않도록 f- 문자열로 해석하는 방법이 있습니까?

기능 (제한 사항 포함) 외에는 .format.


답변

문자열을 f- 문자열 (전체 기능 포함)로 평가하는 간결한 방법은 다음 함수를 사용하는 것입니다.

def fstr(template):
    return eval(f"f'{template}'")

그런 다음 다음을 수행 할 수 있습니다.

template_a = "The current name is {name}"
names = ["foo", "bar"]
for name in names:
    print(fstr(template_a))
# The current name is foo
# The current name is bar

또한 제안 된 다른 많은 솔루션과 달리 다음을 수행 할 수도 있습니다.

template_b = "The current name is {name.upper() * 2}"
for name in names:
    print(fstr(template_b))
# The current name is FOOFOO
# The current name is BARBAR


답변

f- 문자열은 형식이 지정된 문자열을 만드는보다 간결한 방법 .format(**names)으로 f. 문자열이 이러한 방식으로 즉시 평가되는 것을 원하지 않으면 f- 문자열로 만들지 마십시오. 일반 문자열 리터럴로 저장 한 다음format , 그랬듯이 보간을 수행하고 싶을 때 나중에 하십시오.

물론 대안이 있습니다. eval .

template.txt:

f ‘현재 이름은 {name}’입니다.

암호:

>>> template_a = open('template.txt').read()
>>> names = 'foo', 'bar'
>>> for name in names:
...     print(eval(template_a))
...
The current name is foo
The current name is bar

그러나 모든 당신은 대체하면됩니다 관리했습니다 str.formateval확실히 그럴 가치가 없어이다. format호출시 일반 문자열을 계속 사용 하십시오.


답변

.format을 사용하는 것은이 질문에 대한 정답이 아닙니다. 파이썬 f- 문자열은 str.format () 템플릿과 매우 다릅니다 … 그들은 코드 나 다른 값 비싼 작업을 포함 할 수 있습니다. 따라서 지연이 필요합니다.

다음은 지연된 로거의 예입니다. 이것은 logging.getLogger의 일반적인 프리앰블을 사용하지만 로그 수준이 올바른 경우에만 f- 문자열을 해석하는 새 함수를 추가합니다.

log = logging.getLogger(__name__)

def __deferred_flog(log, fstr, level, *args):
    if log.isEnabledFor(level):
        import inspect
        frame = inspect.currentframe().f_back.f_back
        try:
            fstr = 'f"' + fstr + '"'
            log.log(level, eval(fstr, frame.f_globals, frame.f_locals))
        finally:
            del frame
log.fdebug = lambda fstr, *args: __deferred_flog(log, fstr, logging.DEBUG, *args)
log.finfo = lambda fstr, *args: __deferred_flog(log, fstr, logging.INFO, *args)

이것은 log.fdebug("{obj.dump()}")디버깅이 활성화되어 있지 않으면 객체를 덤프하지 않고 …. 과 같은 일을 할 수 있다는 장점이 있습니다 .

IMHO : 이것은 f- 문자열 의 기본 작업 이어야 했지만 지금은 너무 늦었습니다 . F- 문자열 평가는 방대하고 의도하지 않은 부작용을 가질 수 있으며 지연된 방식으로 발생하면 프로그램 실행이 변경됩니다.

f- 문자열을 적절하게 지연 시키려면 파이썬이 명시 적으로 동작을 전환하는 방법이 필요합니다. 문자 ‘g’를 사용할까요? 😉

문자열 변환기에 버그가 있으면 지연된 로깅이 충돌하지 않아야한다는 지적이 있습니다. 위의 솔루션은 물론 이렇게하려면 변경할 수 있습니다 finally:except: , 그리고 스틱 log.exception거기에.


답변

당신이 원하는 것은 파이썬 향상 으로 간주되는 것 같습니다 .

한편-연결된 토론에서-다음은 사용할 필요가없는 합리적인 해결 방법 인 것 같습니다 eval().

class FL:
    def __init__(self, func):
        self.func = func
    def __str__(self):
        return self.func()


template_a = FL(lambda: f"The current name, number is {name!r}, {number+1}")
names = "foo", "bar"
numbers = 40, 41
for name, number in zip(names, numbers):
    print(template_a)

산출:

The current name, number is 'foo', 41
The current name, number is 'bar', 42


답변

kadee답변에서 영감을 얻은 다음은 deferred-f-string 클래스를 정의하는 데 사용할 수 있습니다.

class FStr:
    def __init__(self, s):
        self._s = s
    def __repr__(self):
        return eval(f"f'{self._s}'")

...

template_a = FStr('The current name is {name}')

names = ["foo", "bar"]
for name in names:
    print (template_a)

정확히 질문이 요청한 것입니다