एफ-स्ट्रिंग्स के मूल्यांकन को स्थगित / स्थगित कैसे करें?


99

मैं कुछ फ़ाइलों को उत्पन्न करने के लिए टेम्प्लेट स्ट्रिंग का उपयोग कर रहा हूं और मुझे इस उद्देश्य के लिए नए एफ-स्ट्रिंग्स की संक्षिप्तता पसंद है, मेरे पिछले टेम्पलेट कोड को कुछ इस तरह से कम करने के लिए:

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}")

हालांकि, कभी-कभी यह समझ में आता है कि टेम्पलेट को कहीं और परिभाषित किया गया है - कोड में उच्चतर, या फ़ाइल या किसी चीज़ से आयात किया गया। इसका मतलब यह है कि टेम्प्लेट एक स्थिर स्ट्रिंग है जिसमें इसमें टैगिंग प्रारूपित हैं। नए एफ-स्ट्रिंग के रूप में स्ट्रिंग की व्याख्या करने के लिए दुभाषिया को बताने के लिए स्ट्रिंग के साथ कुछ होना होगा, लेकिन मुझे नहीं पता कि क्या ऐसी कोई चीज है।

क्या किसी स्ट्रिंग में लाने का कोई तरीका है और क्या उसने .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}

5
आप एक fस्ट्रिंग के साथ ऐसा नहीं कर सकते । एक fस्ट्रिंग डेटा नहीं है, और यह निश्चित रूप से एक स्ट्रिंग नहीं है; यह कोड है। (इसे disमॉड्यूल के साथ जांचें ।) यदि आप चाहते हैं कि कोड का मूल्यांकन बाद में किया जाए, तो आप एक फ़ंक्शन का उपयोग करते हैं।
टाइप करें

12
FYI करें, PEP 501 ने आपके पहले आदर्श के करीब एक सुविधा का प्रस्ताव रखा, लेकिन यह वर्तमान में [f-strings] के साथ लंबित और अधिक अनुभव को स्थगित कर दिया है।
jwodder

एक टेम्पलेट एक स्थिर स्ट्रिंग है, लेकिन एक f-string एक स्ट्रिंग नहीं है, यह एक कोड ऑब्जेक्ट है, जैसा कि @kindall ने कहा है। मुझे लगता है कि एक एफ-स्ट्रिंग तुरंत वैरिएबल के खिलाफ बंधी होती है जब यह तुरंत (पायथन 3.6,7 में) होती है, तब नहीं जब इसे अंततः उपयोग किया जाता है। तो f-string आपके बदसूरत पुराने की तुलना में कम उपयोगी हो सकती है .format(**locals()), हालांकि कॉस्मेटोलॉजीक रूप से अच्छे। PEP-501 लागू होने तक।
21

गुइडो हमें बचाते हैं, लेकिन पीईपी 498 ने वास्तव में इसे रोक दिया हैपीईपी 501 द्वारा वर्णित आस्थगित मूल्यांकन को मूल एफ-स्ट्रिंग कार्यान्वयन में बेक किया जाना चाहिए। अब हम एक कम फीचरफुल, बेहद धीमी गति str.format()से एक ओर आस्थगित मूल्यांकन का समर्थन करने वाले और दूसरी ओर अधिक आस्थगित मूल्यांकन का समर्थन नहीं करने वाले और अधिक फीचरफुल, बेहद फास्ट एफ-स्ट्रिंग सिंटैक्स के बीच परेशान कर रहे हैं । इसलिए हमें अभी भी दोनों की आवश्यकता है और पायथन में अभी भी कोई मानक स्ट्रिंग फॉर्मेटर नहीं है। Xkcd मानक मेम डालें।
सेसिल करी

जवाबों:


26

यहाँ एक पूर्ण "आदर्श 2" है।

यह एफ-स्ट्रिंग नहीं है - यह एफ-स्ट्रिंग्स का भी उपयोग नहीं करता है - लेकिन यह अनुरोध के अनुसार करता है। सिंटैक्स बिल्कुल निर्दिष्ट के रूप में। कोई सुरक्षा सिरदर्द नहीं है क्योंकि हम उपयोग नहीं कर रहे हैं eval()

यह एक छोटे वर्ग और उपकरणों का उपयोग करता है __str__जिसे स्वचालित रूप से प्रिंट द्वारा कहा जाता है। कक्षा के सीमित दायरे से बचने के लिए हम 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

13
मैं इसे उत्तर के रूप में स्वीकार करने जा रहा हूं, हालांकि मुझे नहीं लगता कि मैं वास्तव में चरम चालाकी के कारण इसे कोड में उपयोग करूंगा। खैर शायद कभी नहीं :)। हो सकता है कि अजगर लोग पीईपी 501 के कार्यान्वयन के लिए इसका इस्तेमाल कर सकते हैं । यदि मेरे प्रश्न थे "मुझे इस परिदृश्य को कैसे संभालना चाहिए" तो उत्तर होगा "बस .format () फ़ंक्शन का उपयोग करते रहें और PEP 501 को हल करने के लिए प्रतीक्षा करें।" यह पता लगाने के लिए धन्यवाद कि क्या नहीं किया जाना चाहिए, @PaPPanzer
JDAnders

6
यह तब काम नहीं करता है जब टेम्पलेट में सरल चर नामों की तुलना में कुछ अधिक जटिल शामिल होता है। उदाहरण के लिए: template = "The beginning of the name is {name[:4]}"(-> TypeError: string indices must be integers)
bli

6
@ लिली दिलचस्प, की एक सीमा लगती है str.format। मुझे लगता था कि f- स्ट्रिंग्स सिर्फ कुछ के लिए सिंटैक्टिक शुगर है, str.format(**locals(), **globals())लेकिन जाहिर है कि मैं गलत था।
पॉल पैंजर

4
कृपया इसका उपयोग उत्पादन में न करें। inspectलाल झंडा है।
एलेक्जेंडर्नस्ट

1
मेरे पास 2 प्रश्न हैं, उत्पादन के लिए एक "लाल झंडे" का निरीक्षण क्यों किया जाता है जैसे कि यह एक अपवाद होगा या अधिक व्यवहार्य वर्कअराउंड होगा? और क्या __slots__कम स्मृति उपयोग के लिए यहां उपयोग के खिलाफ कुछ है ?
जाब

21

इसका मतलब यह है कि टेम्प्लेट एक स्थिर स्ट्रिंग है जिसमें इसमें टैगिंग स्वरूपण हैं

हां, यही कारण है कि हमारे पास प्रतिस्थापन फ़ील्ड के साथ शाब्दिक हैं और .formatइसलिए, जब भी हम formatइसे पसंद करते हैं, तब हम फ़ील्ड को प्रतिस्थापित कर सकते हैं ।

स्ट्रिंग को नए एफ-स्ट्रिंग के रूप में व्याख्या करने के लिए दुभाषिया को बताने के लिए स्ट्रिंग के साथ कुछ करना होगा

यही उपसर्ग है 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())कॉल का उपयोग करने से बचने के लिए एक एफ-स्ट्रिंग के रूप में व्याख्या की है ?

एक फ़ंक्शन (सीमाएं शामिल) के अलावा, नहीं, तो साथ ही साथ छड़ी हो सकती है .format


मजेदार, मैंने ठीक वही स्निपेट पोस्ट किया था। लेकिन मैंने इसे सीमाओं को तोड़ने के कारण वापस ले लिया। (फंक्शन में लूप के लिए रैपिंग की कोशिश करें।)
पॉल

@PaulPanzer क्या आप शायद प्रश्न को संपादित करना चाहते हैं और इसे फिर से शामिल करना चाहते हैं? मुझे उत्तर हटाने में कोई दिक्कत नहीं होगी। यह ओपी के मामले के लिए एक व्यवहार्य विकल्प है, यह सभी मामलों के लिए एक व्यवहार्य विकल्प नहीं है , यह डरपोक है।
दिमित्रिस फासरकिस हिलियार्ड

1
नहीं, यह ठीक है, इसे रखो। मैं अपने नए समाधान के साथ बहुत खुश हूं। लेकिन मैं आपकी बात देख सकता हूं कि यह एक व्यवहार्य है यदि आप इसकी सीमाओं के बारे में जानते हैं। हो सकता है कि आप अपनी पोस्ट में थोड़ी चेतावनी जोड़ सकते हैं ताकि कोई भी गलत तरीके से अपने पैर को गोली न मार सके?
पॉल पैंजर

16

निम्नलिखित कार्य का उपयोग करते हुए एक स्ट्रिंग का मूल्यांकन एक एफ-स्ट्रिंग (अपनी पूर्ण क्षमताओं के साथ) के रूप में किया जाता है:

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

4
अब तक का सबसे अच्छा जवाब! जब उन्होंने एफ-स्ट्रिंग्स की शुरुआत की, तो उन्होंने इस सरल कार्यान्वयन को एक अंतर्निहित सुविधा के रूप में शामिल नहीं किया?
user3204459

1
नहींं, यह गुंजाइश खो देता है। एकमात्र कारण जो काम करता है nameवह है वैश्विक। f-strings को मूल्यांकन में टाल दिया जाना चाहिए , लेकिन क्लास FString को कॉल करने वालों और स्थानीय लोगों को देखकर scoped तर्कों के संदर्भों की एक सूची बनाने की आवश्यकता है ... और फिर उपयोग किए जाने पर स्ट्रिंग का मूल्यांकन करें।
एरिक एरोनिटी

2
@ user3204459: मनमाने तारों को निष्पादित करने में सक्षम होने के कारण स्वाभाविक रूप से एक सुरक्षा खतरा है - जिसके कारण eval()आमतौर पर इसका उपयोग हतोत्साहित किया जाता है।
15

2
@ स्मार्टिन्यू में अजगर की एक विशेषता होनी चाहिए ताकि आपको eval का उपयोग करने की आवश्यकता न पड़े ... इसके अलावा, f-string में eval () के समान जोखिम हैं क्योंकि आप दुर्भावनापूर्ण कोड सहित घुंघराले कोष्ठक में कुछ भी डाल सकते हैं, यदि ऐसा है तो एक चिंता का विषय है कि एफ-स्ट्रिंग्स का उपयोग न करें
user3204459

2
यह वही है जो मैं देख रहा था, 'fstr postpone' के लिए ducking। Eval सामान्य रूप से fstrings के उपयोग से बदतर नहीं लगता है, जैसा कि वे कहते हैं, मुझे लगता है कि दोनों के पास एक ही शक्ति है: f "{eval ('print (42) ')} "
user2692263

12

एक एफ-स्ट्रिंग केवल एक स्वरूपित स्ट्रिंग बनाने का एक अधिक संक्षिप्त तरीका है, .format(**names)जिसके साथ प्रतिस्थापित किया जाता है f। यदि आप नहीं चाहते कि एक स्ट्रिंग का तुरंत इस तरह से मूल्यांकन किया जाए, तो इसे F-string न बनाएं। इसे एक साधारण स्ट्रिंग शाब्दिक के रूप में सहेजें, और फिर बाद में कॉल करें formatजब आप प्रक्षेप करना चाहते हैं, जैसा कि आप कर रहे हैं।

बेशक, के साथ एक विकल्प है eval

template.txt:

f 'वर्तमान नाम {नाम}' है

कोड:

>>> 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कॉल के साथ नियमित स्ट्रिंग्स का उपयोग करते रहें ।


3
मुझे आपके कोड के स्निपेट में वास्तव में कोई फायदा नहीं दिख रहा है। मेरा मतलब है, आप हमेशा फ़ाइल के The current name is {name}अंदर लिख सकते हैं template.txtऔर फिर उपयोग print(template_a.format(name=name))(या .format(**locals())) कर सकते हैं। कोड लगभग 10 वर्ण लंबा है, लेकिन यह किसी भी संभावित सुरक्षा समस्याओं का परिचय नहीं देता है eval
बाकुरि

@ बकुरीउ - हाँ; जैसा मैंने कहा, हालांकि evalहमें लिखने f'{name}'और nameवांछित होने तक मूल्यांकन में देरी करने की अनुमति देता है , यह केवल एक नियमित टेम्पलेट स्ट्रिंग बनाने और फिर उस formatपर कॉल करने से हीन है , जैसा कि ओपी पहले से ही कर रहा था।
टाइगरहॉक 3

4
"एफ-स्ट्रिंग एक फॉर्मैटेड स्ट्रिंग बनाने का एक अधिक संक्षिप्त तरीका है, एफ के साथ .format (** नाम) की जगह।" बिल्कुल नहीं - वे विभिन्न वाक्यविन्यास का उपयोग करते हैं। मेरे पास जांच के लिए हाल ही में पर्याप्त python3 नहीं है, लेकिन उदाहरण के लिए मुझे विश्वास है कि f '{a + b}' काम करता है, जबकि '{a + b}' प्रारूप। (a = a, b = b) KeyError को बढ़ाता है। । .format () शायद कई संदर्भों में ठीक है, लेकिन यह एक ड्रॉप-इन प्रतिस्थापन नहीं है।
२०:०२ पर दार्शन

2
@ मुझे लगता है कि मुझे सिर्फ एक उदाहरण का सामना करना पड़ा जहां .formatएक एफ-स्ट्रिंग के बराबर नहीं है, जो आपको टिप्पणी का समर्थन कर सकता है DNA = "TATTCGCGGAAAATATTTTGA"; fragment = f"{DNA[2:8]}"; failed_fragment = "{DNA[2:8]}".format(**locals()):। failed_fragmentमें परिणाम बनाने का प्रयास TypeError: string indices must be integers
bli

12

.Format का उपयोग करना इस प्रश्न का सही उत्तर नहीं है। पायथन एफ-स्ट्रिंग्स 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: यह एफ-स्ट्रिंग्स का डिफ़ॉल्ट संचालन होना चाहिए था , हालांकि अब बहुत देर हो चुकी है । एफ-स्ट्रिंग मूल्यांकन में बड़े पैमाने पर और अनपेक्षित दुष्परिणाम हो सकते हैं, और ऐसा होने पर आस्थगित तरीके से कार्यक्रम निष्पादन बदल जाएगा।

एफ-स्ट्रिंग्स को ठीक से स्थगित करने के लिए, अजगर को स्पष्ट रूप से स्विचिंग व्यवहार के कुछ तरीके की आवश्यकता होगी। शायद 'जी' अक्षर का उपयोग करें? ;)


इस जवाब से पूरी ईमानदारी से सहमत हैं। यह उपयोग मामला वह है जो मैं इस प्रश्न की खोज करते समय सोच रहा था।
justhalf

यह सही जवाब है। कुछ समय: %timeit log.finfo(f"{bar=}") 91.9 µs ± 7.45 µs per loop %timeit log.info(f"{bar=}") 56.2 µs ± 630 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) log.setLevel(logging.CRITICAL) %timeit log.finfo("{bar=}") 575 ns ± 2.9 ns per loop %timeit log.info(f"{bar=}") 480 ns ± 9.37 ns per loop %timeit log.finfo("") 571 ns ± 2.66 ns per loop %timeit log.info(f"") 380 ns ± 0.92 ns per loop %timeit log.info("") 367 ns ± 1.65 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
जलेक्स

8

आप जो चाहते हैं वह एक पायथन वृद्धि के रूप में माना जा रहा है ।

इस बीच, लिंक की गई चर्चा से - निम्नलिखित ऐसा लगता है कि यह एक उचित समाधान होगा जिसका उपयोग करने की आवश्यकता नहीं है 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

7

से प्रेरित Kadee द्वारा जवाब , निम्नलिखित एक आस्थगित-च-स्ट्रिंग वर्ग को परिभाषित करने के लिए इस्तेमाल किया जा सकता।

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)

जो वास्तव में क्या सवाल के लिए पूछा है


4

या शायद एफ-स्ट्रिंग्स का उपयोग न करें, बस प्रारूप:

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))

यह सभी मामलों में काम नहीं करता है। उदाहरण: fun = "{DNA[2:8]}".format; DNA = "TATTCGCGGAAAATATTTTGA"; fun(DNA=DNA)। ->TypeError: string indices must be integers
bli

लेकिन यह सामान्य उपयोग में भी काम नहीं करता है, कृपया उत्तर stackoverflow.com/questions/14072810/… पर देखें
msztolcman


0

एक सुझाव जो एफ-स्ट्रिंग्स का उपयोग करता है। अपने मूल्यांकन को उस तार्किक स्तर पर करें जहाँ टेम्प्लेटिंग हो रही है और इसे जनरेटर के रूप में पास करें। आप एफ-स्ट्रिंग्स का उपयोग करके, जो भी आप चुनते हैं, उसे खोल सकते हैं

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  
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.