programing

어떻게 f-string 평가를 연기/연기합니까?

abcjava 2023. 5. 11. 21:00
반응형

어떻게 f-string 평가를 연기/연기합니까?

템플릿 문자열을 사용하여 일부 파일을 생성하고 있으며 이전 템플릿 코드를 다음과 같이 줄일 수 있는 새로운 f-string의 간결성이 좋습니다.

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-string으로 해석하라고 하려면 문자열에 무슨 일이 일어나야 할 텐데, 그런 일이 있는지 모르겠습니다.

을 가져와서 f-string을 할 수 ?.format(**locals())전화요?

이상적으로 나는 이렇게 코딩할 수 있기를 원합니다.(어디서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}

문자열을 f-string으로 평가하는 간단한 방법은 다음 함수를 사용하는 것입니다.

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

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

아니라합니다.f-string은 f-string을 사용합니다.지정된 구문과 정확하게 일치합니다.사용하지 않기 때문에 보안 문제가 없습니다.eval().

그것은 약간의 수업을 사용하고 실행합니다.__str__인쇄에 의해 자동으로 호출됩니다.는 수의제범벗위위어기사해다우용니합리는나한를업된▁the다▁to▁we니사▁use용합▁class▁the▁of우▁escape리는위를 사용합니다.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-string으로 해석하도록 하려면 문자열에 어떤 일이 발생해야 합니다.

그게접야사입니다.f/F함수로 래핑하여 통화 시간 동안 평가를 연기할 수 있지만, 물론 추가 비용이 발생합니다.

def template_a():
    return 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

그러나 교체할 때는 글로벌 네임스페이스만 볼 수 있다는 점에서 문제를 느끼고 제한을 받습니다.지역 이름이 필요한 상황에서 사용하려고 하는 것은 (완전히 요점을 벗어난) 인수로 문자열에 전달되지 않는 한 비참하게 실패할 것입니다.

을 가져와서 f-string을 할 수 ?.format(**locals())전화요?

기능(제한 포함) 이외에는 아니요, 그래서 계속하는 것이 나을 수 있습니다..format.

어때요?

s = 'Hi, {foo}!'

s
> 'Hi, {foo}!'

s.format(foo='Bar')
> 'Hi, Bar!'

.format을 사용하는 것은 이 질문에 대한 정답이 아닙니다.Python f-string은 str.format() 템플릿과 매우 다릅니다...코드 또는 기타 값비싼 작업을 포함할 수 있으므로 연기가 필요합니다.

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

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-string의 기본 동작이어야 했지만, 지금은 너무 늦었습니다.F-string 평가는 막대한 부작용과 의도하지 않은 부작용을 초래할 수 있으며, 지연된 방식으로 발생하면 프로그램 실행이 변경됩니다.

f-string을 적절하게 지연시키려면 python은 동작을 명시적으로 전환하는 어떤 방법이 필요합니다.'g'자를 사용해도 될까요?;)

문자열 변환기에 버그가 있을 경우 지연 로깅이 중단되지 않아야 한다는 지적이 제기되었습니다.위의 솔루션도 이를 수행할 수 있습니다. 변경할 수 있습니다.finally:except:그리고 a를 붙입니다.log.exception저 안에

f-string은 단순히 형식화된 문자열을 만드는 보다 간결한 방법입니다..format(**names)와 함께f문자열을 이러한 방식으로 즉시 평가하지 않으려면 문자열을 f-string으로 만들지 마십시오.리터럴로 에 일반문리로저다장한호음출럴터열을 호출합니다.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.format와 함께eval그럴만한 가치가 없는 것은 분명합니다.일반 문자열을 계속 사용합니다.format콜.콜.

당신이 원하는 것은 파이썬 향상으로 간주되고 있는 것으로 보입니다.

한편, 링크된 토론에서 볼 때, 다음은 사용할 필요가 없는 합리적인 해결 방법인 것 같습니다.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

카데의 답변에서 영감을 받아, 다음을 사용하여 지연된 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)

그것이 정확히 질문이 요구한 것입니다.

또는 f-string을 사용하지 않고 형식만 지정할 수 있습니다.

fun = "The curent name is {name}".format
names = ["foo", "bar"]
for name in names:
    print(fun(name=name))

이름이 없는 버전:

fun = "The curent name is {}".format
names = ["foo", "bar"]
for name in names:
    print(fun(name))

이 대답들의 대부분은 여러분이 가끔은 현을 빼는 것처럼 행동하는 것을 얻을 것이지만, 어떤 경우에는 모두 잘못될 것입니다.Pypi에 소포가 있습니다.f-yeah그것은 이 모든 것을 하고, 당신에게 단지 두 개의 추가적인 캐릭터만 비용이 듭니다! (완전한 공개, 나는 작가입니다)

from fyeah import f

print(f("""'{'"all" the quotes'}'"""))

f-string과 형식 호출 사이에는 많은 차이가 있습니다. 여기에는 아마도 불완전한 목록이 있습니다.

  • f-paths를 사용하면 python 코드의 임의 평가가 가능
  • f-strings는 식에 백슬래시를 포함할 수 없습니다(포맷된 문자열에는 식이 없기 때문에 이것은 차이가 없지만 원시 평가()와 다를 수 있습니다).
  • 형식이 지정된 문자열의 dict 조회를 따옴표로 묶어서는 안 됩니다.f-string에서 dict 조회를 인용할 수 있으므로 문자열이 아닌 키도 조회할 수 있습니다.
  • 에는 format가 없는 . f-filename 형식은 다음과 같습니다.f"The argument is {spam=}"
  • f-string 식은 비워 둘 수 없습니다.

eval을 사용하라는 제안은 완전한 f-string 형식을 지원하지만 모든 문자열 유형에서 작동하지는 않습니다.

def f_template(the_string):
    return eval(f"f'{the_string}'")

print(f_template('some "quoted" string'))
print(f_template("some 'quoted' string"))
some "quoted" string
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f_template
  File "<string>", line 1
    f'some 'quoted' string'
            ^
SyntaxError: invalid syntax

이 예제에서는 경우에 따라 변수 범위 지정이 잘못될 수도 있습니다.

이를 위해 다음과 같은 람다 함수 안에 fstring을 사용하는 것을 선호합니다.

s = lambda x: f'this is your string template to embed {x} in it.'
n = ['a' , 'b' , 'c']
for i in n:
   print( s(i) )

를 사용하는 것에 대해 많은 이야기가 있습니다.str.format()그러나 언급했듯이 산술이나 슬라이스와 같은 f-변수에서 허용되는 대부분의 식을 허용하지 않습니다.용사를 합니다.eval()분명히 단점도 있습니다.

저는 진자와 같은 템플릿 언어를 알아보는 것을 추천합니다.제 경우에는 꽤 잘 작동합니다.변수 주석 구문을 f-string 구문과 일치하도록 단일 곱슬곱슬한 대괄호로 재정의한 아래 예제를 참조하십시오.이렇게 호출된 f-string과 진자의 차이점을 충분히 검토하지 못했습니다.

from jinja2 import Environment, BaseLoader

a, b, c = 1, 2, "345"
templ = "{a or b}{c[1:]}"

env = Environment(loader=BaseLoader, variable_start_string="{", variable_end_string="}")
env.from_string(templ).render(**locals())

의 결과.

'145'

저는 이 문제가 꽤 흥미로웠고 게으른 f-string을 구현하는 만의 라이브러리를 썼습니다.

설치:

pip install fazy

사용 방법:

import f

number = 33
print(f('{number} kittens drink milk'))

이 솔루션은 예를 들어 로깅에 적합합니다.링크에서 설명서의 기능 및 제한 사항에 대해 자세히 알아보십시오.

f-string을 사용하는 제안입니다.템플릿이 발생하는 논리적 수준에서 평가를 수행하여 생성기로 전달합니다.F-스트링을 사용하여 원하는 지점에서 풀 수 있습니다.

In [46]: names = (i for i in ('The CIO, Reed', 'The homeless guy, Arnot', 'The security guard Spencer'))

In [47]: po = (f'Strangely, {next(names)} has a nice {i}' for i in (" nice house", " fast car", " big boat"))

In [48]: while True:  
...:     try:  
...:         print(next(po))  
...:     except StopIteration:  
...:         break  
...:       
Strangely, The CIO, Reed has a nice  nice house  
Strangely, The homeless guy, Arnot has a nice  fast car  
Strangely, The security guard Spencer has a nice  big boat  

▁a를 사용할 수 ..format스타일 변경 및 교체된 변수 이름을 명시적으로 정의합니다.

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

산출량

The current name is foo
The current name is bar

언급URL : https://stackoverflow.com/questions/42497625/how-to-postpone-defer-the-evaluation-of-f-strings

반응형