जवाबों:
मैपिंग को ओवरराइट करके आप इसे आंशिक स्वरूपण में बदल सकते हैं:
import string
class FormatDict(dict):
def __missing__(self, key):
return "{" + key + "}"
s = '{foo} {bar}'
formatter = string.Formatter()
mapping = FormatDict(foo='FOO')
print(formatter.vformat(s, (), mapping))
मुद्रण
FOO {bar}
बेशक यह बुनियादी कार्यान्वयन केवल बुनियादी मामलों के लिए सही ढंग से काम करता है।
__missing__()
, __format__()
प्रारूप युक्ति सहित मूल प्लेसहोल्डर को वापस करने के लिए एक तरह से ओवरराइडिंग कस्टम वर्ग की एक आवृत्ति लौटाएं। अवधारणा का प्रमाण: ideone.com/xykV7R
यदि आप जानते हैं कि आप किस क्रम में चीजों को प्रारूपित कर रहे हैं:
s = '{foo} {{bar}}'
इसे इस तरह उपयोग करें:
ss = s.format(foo='FOO')
print ss
>>> 'FOO {bar}'
print ss.format(bar='BAR')
>>> 'FOO BAR'
आप निर्दिष्ट नहीं कर सकते हैं foo
और bar
एक ही समय में - आपको इसे क्रमिक रूप से करना होगा।
s.format(foo='FOO',bar='BAR')
तो मुझे अभी भी मिला 'FOO {bar}'
, कोई फर्क नहीं पड़ता। क्या आप इसे स्पष्ट कर सकते हैं?
आप उस partial
फ़ंक्शन का उपयोग कर सकते हैं, functools
जो संक्षिप्त है, सबसे अधिक पठनीय है और कोडर के इरादे का भी वर्णन करता है:
from functools import partial
s = partial("{foo} {bar}".format, foo="FOO")
print s(bar="BAR")
# FOO BAR
python from functool import partial s = "{foo} {bar}".format s_foo = partial(s, foo="FOO") print(s_foo(bar="BAR")) # FOO BAR print(s(foo="FOO", bar="BAR")) # FOO BAR
partial()
अगर मुझे आंशिक रूप से स्वरूपित स्ट्रिंग (वह है "FOO {bar}"
) के साथ कुछ प्रसंस्करण करने की आवश्यकता है, तो मेरी मदद करने वाला नहीं है ।
"{foo} {{bar}}".format(foo="{bar}").format(bar="123")
अन्य उदाहरणों से। मुझे उम्मीद होगी "{bar} 123"
लेकिन वे आउटपुट देंगे "123 123"
।
इस सीमा की .format()
- आंशिक प्रतिस्थापन करने में असमर्थता - मुझे डगमगा रही है।
Formatter
यहाँ कई उत्तरों में वर्णित एक कस्टम क्लास लिखने का मूल्यांकन करने के बाद और यहाँ तक कि तीसरे पक्ष के पैकेज जैसे कि lazy_format का उपयोग करने पर विचार करने के बाद , मैंने एक बहुत सरल इनबिल्ट समाधान की खोज की: टेम्पलेट स्ट्रिंग्स
यह समान कार्यक्षमता प्रदान करता है लेकिन आंशिक प्रतिस्थापन पूर्ण safe_substitute()
विधि भी प्रदान करता है । टेम्प्लेट स्ट्रिंग्स के लिए एक $
उपसर्ग (जो थोड़ा अजीब लगता है - लेकिन मुझे लगता है कि समग्र समाधान बेहतर है) की आवश्यकता है।
import string
template = string.Template('${x} ${y}')
try:
template.substitute({'x':1}) # raises KeyError
except KeyError:
pass
# but the following raises no error
partial_str = template.safe_substitute({'x':1}) # no error
# partial_str now contains a string with partial substitution
partial_template = string.Template(partial_str)
substituted_str = partial_template.safe_substitute({'y':2}) # no error
print substituted_str # prints '12'
इसके आधार पर एक सुविधा आवरण तैयार किया:
class StringTemplate(object):
def __init__(self, template):
self.template = string.Template(template)
self.partial_substituted_str = None
def __repr__(self):
return self.template.safe_substitute()
def format(self, *args, **kws):
self.partial_substituted_str = self.template.safe_substitute(*args, **kws)
self.template = string.Template(self.partial_substituted_str)
return self.__repr__()
>>> s = StringTemplate('${x}${y}')
>>> s
'${x}${y}'
>>> s.format(x=1)
'1${y}'
>>> s.format({'y':2})
'12'
>>> print s
12
इसी प्रकार स्वेन के उत्तर पर आधारित एक आवरण जो डिफ़ॉल्ट स्ट्रिंग प्रारूपण का उपयोग करता है:
class StringTemplate(object):
class FormatDict(dict):
def __missing__(self, key):
return "{" + key + "}"
def __init__(self, template):
self.substituted_str = template
self.formatter = string.Formatter()
def __repr__(self):
return self.substituted_str
def format(self, *args, **kwargs):
mapping = StringTemplate.FormatDict(*args, **kwargs)
self.substituted_str = self.formatter.vformat(self.substituted_str, (), mapping)
यदि आप अपने स्वयं को परिभाषित करते हैं Formatter
जो get_value
विधि को ओवरराइड करता है, तो आप इसका उपयोग कर सकते हैं कि जो कुछ भी आप चाहते थे, अपरिभाषित फ़ील्ड नामों को मैप करने के लिए:
http://docs.python.org/library/string.html#string.Formatter.get_value
उदाहरण के लिए, आप मैप कर सकते bar
करने के लिए "{bar}"
करता है, तो bar
kwargs में नहीं है।
हालाँकि, इसके लिए format()
आपके फ़ॉर्मेटर ऑब्जेक्ट की विधि का उपयोग करने की आवश्यकता है , न कि स्ट्रिंग की format()
विधि की।
>>> 'fd:{uid}:{{topic_id}}'.format(uid=123)
'fd:123:{topic_id}'
इसे आजमाएं।
{{
और }}
स्वरूपण के निशान से बचने का एक तरीका है, इसलिए है format()
प्रतिस्थापन और बदलने वाला प्रदर्शन नहीं करता {{
और }}
साथ {
और }
क्रमश:।
{{ }}
केवल एक प्रारूप के लिए काम करता है, यदि आपको अधिक आवेदन करने की आवश्यकता है तो आपको अधिक जोड़ने की आवश्यकता होगी {}
। पूर्व। 'fd:{uid}:{{topic_id}}'.format(uid=123).format(a=1)
दूसरा प्रारूप topic_id
मान प्रदान नहीं कर रहा है क्योंकि त्रुटि वापस आएगी ।
अम्बर की टिप्पणी के लिए धन्यवाद , मैं इस के साथ आया:
import string
try:
# Python 3
from _string import formatter_field_name_split
except ImportError:
formatter_field_name_split = str._formatter_field_name_split
class PartialFormatter(string.Formatter):
def get_field(self, field_name, args, kwargs):
try:
val = super(PartialFormatter, self).get_field(field_name, args, kwargs)
except (IndexError, KeyError, AttributeError):
first, _ = formatter_field_name_split(field_name)
val = '{' + field_name + '}', first
return val
{field!s: >4}
बन जाता है{field}
मेरे लिए यह काफी अच्छा था:
>>> ss = 'dfassf {} dfasfae efaef {} fds'
>>> nn = ss.format('f1', '{}')
>>> nn
'dfassf f1 dfasfae efaef {} fds'
>>> n2 = nn.format('whoa')
>>> n2
'dfassf f1 dfasfae efaef whoa fds'
सभी समाधान मैंने पाया है कि अधिक उन्नत कल्पना या रूपांतरण विकल्पों के साथ समस्याएँ हैं। @ SvenMarnach का फॉर्मेटप्लेहोल्डर शानदार रूप से चतुर है लेकिन यह जबरदस्ती (जैसे {a!s:>2s}
) के साथ ठीक से काम नहीं करता है क्योंकि यह __str__
इसके बजाय विधि (इस उदाहरण में) को कॉल करता है __format__
और आप किसी भी अतिरिक्त स्वरूपण को खो देते हैं।
यहाँ मैं इसके साथ समाप्त हुआ और इसकी कुछ प्रमुख विशेषताएं हैं:
sformat('The {} is {}', 'answer')
'The answer is {}'
sformat('The answer to {question!r} is {answer:0.2f}', answer=42)
'The answer to {question!r} is 42.00'
sformat('The {} to {} is {:0.{p}f}', 'answer', 'everything', p=4)
'The answer to everything is {:0.4f}'
str.format
(केवल मैपिंग नहीं){k!s}
{!r}
{k:>{size}}
{k.foo}
{k[0]}
{k!s:>{size}}
import string
class SparseFormatter(string.Formatter):
"""
A modified string formatter that handles a sparse set of format
args/kwargs.
"""
# re-implemented this method for python2/3 compatibility
def vformat(self, format_string, args, kwargs):
used_args = set()
result, _ = self._vformat(format_string, args, kwargs, used_args, 2)
self.check_unused_args(used_args, args, kwargs)
return result
def _vformat(self, format_string, args, kwargs, used_args, recursion_depth,
auto_arg_index=0):
if recursion_depth < 0:
raise ValueError('Max string recursion exceeded')
result = []
for literal_text, field_name, format_spec, conversion in \
self.parse(format_string):
orig_field_name = field_name
# output the literal text
if literal_text:
result.append(literal_text)
# if there's a field, output it
if field_name is not None:
# this is some markup, find the object and do
# the formatting
# handle arg indexing when empty field_names are given.
if field_name == '':
if auto_arg_index is False:
raise ValueError('cannot switch from manual field '
'specification to automatic field '
'numbering')
field_name = str(auto_arg_index)
auto_arg_index += 1
elif field_name.isdigit():
if auto_arg_index:
raise ValueError('cannot switch from manual field '
'specification to automatic field '
'numbering')
# disable auto arg incrementing, if it gets
# used later on, then an exception will be raised
auto_arg_index = False
# given the field_name, find the object it references
# and the argument it came from
try:
obj, arg_used = self.get_field(field_name, args, kwargs)
except (IndexError, KeyError):
# catch issues with both arg indexing and kwarg key errors
obj = orig_field_name
if conversion:
obj += '!{}'.format(conversion)
if format_spec:
format_spec, auto_arg_index = self._vformat(
format_spec, args, kwargs, used_args,
recursion_depth, auto_arg_index=auto_arg_index)
obj += ':{}'.format(format_spec)
result.append('{' + obj + '}')
else:
used_args.add(arg_used)
# do any conversion on the resulting object
obj = self.convert_field(obj, conversion)
# expand the format spec, if needed
format_spec, auto_arg_index = self._vformat(
format_spec, args, kwargs,
used_args, recursion_depth-1,
auto_arg_index=auto_arg_index)
# format the object and append to the result
result.append(self.format_field(obj, format_spec))
return ''.join(result), auto_arg_index
def sformat(s, *args, **kwargs):
# type: (str, *Any, **Any) -> str
"""
Sparse format a string.
Parameters
----------
s : str
args : *Any
kwargs : **Any
Examples
--------
>>> sformat('The {} is {}', 'answer')
'The answer is {}'
>>> sformat('The answer to {question!r} is {answer:0.2f}', answer=42)
'The answer to {question!r} is 42.00'
>>> sformat('The {} to {} is {:0.{p}f}', 'answer', 'everything', p=4)
'The answer to everything is {:0.4f}'
Returns
-------
str
"""
return SparseFormatter().format(s, *args, **kwargs)
मैंने इस पद्धति को व्यवहार करने के तरीके के बारे में कुछ परीक्षण लिखने के बाद विभिन्न कार्यान्वयन के साथ मुद्दों की खोज की। वे नीचे हैं अगर कोई उन्हें व्यावहारिक पाता है।
import pytest
def test_auto_indexing():
# test basic arg auto-indexing
assert sformat('{}{}', 4, 2) == '42'
assert sformat('{}{} {}', 4, 2) == '42 {}'
def test_manual_indexing():
# test basic arg indexing
assert sformat('{0}{1} is not {1} or {0}', 4, 2) == '42 is not 2 or 4'
assert sformat('{0}{1} is {3} {1} or {0}', 4, 2) == '42 is {3} 2 or 4'
def test_mixing_manualauto_fails():
# test mixing manual and auto args raises
with pytest.raises(ValueError):
assert sformat('{!r} is {0}{1}', 4, 2)
def test_kwargs():
# test basic kwarg
assert sformat('{base}{n}', base=4, n=2) == '42'
assert sformat('{base}{n}', base=4, n=2, extra='foo') == '42'
assert sformat('{base}{n} {key}', base=4, n=2) == '42 {key}'
def test_args_and_kwargs():
# test mixing args/kwargs with leftovers
assert sformat('{}{k} {v}', 4, k=2) == '42 {v}'
# test mixing with leftovers
r = sformat('{}{} is the {k} to {!r}', 4, 2, k='answer')
assert r == '42 is the answer to {!r}'
def test_coercion():
# test coercion is preserved for skipped elements
assert sformat('{!r} {k!r}', '42') == "'42' {k!r}"
def test_nesting():
# test nesting works with or with out parent keys
assert sformat('{k:>{size}}', k=42, size=3) == ' 42'
assert sformat('{k:>{size}}', size=3) == '{k:>3}'
@pytest.mark.parametrize(
('s', 'expected'),
[
('{a} {b}', '1 2.0'),
('{z} {y}', '{z} {y}'),
('{a} {a:2d} {a:04d} {y:2d} {z:04d}', '1 1 0001 {y:2d} {z:04d}'),
('{a!s} {z!s} {d!r}', '1 {z!s} {\'k\': \'v\'}'),
('{a!s:>2s} {z!s:>2s}', ' 1 {z!s:>2s}'),
('{a!s:>{a}s} {z!s:>{z}s}', '1 {z!s:>{z}s}'),
('{a.imag} {z.y}', '0 {z.y}'),
('{e[0]:03d} {z[0]:03d}', '042 {z[0]:03d}'),
],
ids=[
'normal',
'none',
'formatting',
'coercion',
'formatting+coercion',
'nesting',
'getattr',
'getitem',
]
)
def test_sformat(s, expected):
# test a bunch of random stuff
data = dict(
a=1,
b=2.0,
c='3',
d={'k': 'v'},
e=[42],
)
assert expected == sformat(s, **data)
मेरा सुझाव निम्नलिखित होगा (पायथन 3.6 के साथ परीक्षण):
class Lazymap(object):
def __init__(self, **kwargs):
self.dict = kwargs
def __getitem__(self, key):
return self.dict.get(key, "".join(["{", key, "}"]))
s = '{foo} {bar}'
s.format_map(Lazymap(bar="FOO"))
# >>> '{foo} FOO'
s.format_map(Lazymap(bar="BAR"))
# >>> '{foo} BAR'
s.format_map(Lazymap(bar="BAR", foo="FOO", baz="BAZ"))
# >>> 'FOO BAR'
अद्यतन:
एक और भी सुंदर तरीका (उपवर्ग dict
और ओवरलोडिंग __missing__(self, key)
) यहाँ दिखाया गया है: https://stackoverflow.com/a/17215533/333403
यह मानते हुए कि आप स्ट्रिंग का उपयोग तब तक नहीं करेंगे जब तक यह पूरी तरह से भर न जाए, आप इस वर्ग की तरह कुछ कर सकते हैं:
class IncrementalFormatting:
def __init__(self, string):
self._args = []
self._kwargs = {}
self._string = string
def add(self, *args, **kwargs):
self._args.extend(args)
self._kwargs.update(kwargs)
def get(self):
return self._string.format(*self._args, **self._kwargs)
उदाहरण:
template = '#{a}:{}/{}?{c}'
message = IncrementalFormatting(template)
message.add('abc')
message.add('xyz', a=24)
message.add(c='lmno')
assert message.get() == '#24:abc/xyz?lmno'
इसे प्राप्त करने का एक और तरीका है, जिसका उपयोग करके format
और %
चर को बदलने के लिए। उदाहरण के लिए:
>>> s = '{foo} %(bar)s'
>>> s = s.format(foo='my_foo')
>>> s
'my_foo %(bar)s'
>>> s % {'bar': 'my_bar'}
'my_foo my_bar'
एक बहुत ही बदसूरत लेकिन मेरे लिए सबसे आसान उपाय सिर्फ करना है:
tmpl = '{foo}, {bar}'
tmpl.replace('{bar}', 'BAR')
Out[3]: '{foo}, BAR'
इस तरह आप अभी भी tmpl
नियमित टेम्पलेट के रूप में उपयोग कर सकते हैं और आंशिक स्वरूपण केवल जरूरत पड़ने पर कर सकते हैं। मैं इस समस्या को भी मोहन राज की तरह एक overkilling समाधान का उपयोग करने के लिए तुच्छ लगता है।
यहाँ और वहाँ से सबसे होनहार समाधान का परीक्षण करने के बाद , मैंने महसूस किया कि उनमें से कोई भी वास्तव में निम्नलिखित आवश्यकताओं को पूरा नहीं करता है:
str.format_map()
टेम्पलेट के लिए पहचाने गए सिंटैक्स का सख्ती से पालन करना ;इसलिए, मैंने अपना समाधान लिखा, जो उपरोक्त आवश्यकताओं को पूरा करता है। ( EDIT : अब @SvenMarnach द्वारा संस्करण - जैसा कि इस उत्तर में बताया गया है - मुझे लगता है कि कोने के मामलों को संभालने की जरूरत है)।
मूल रूप से, मैंने टेम्पलेट स्ट्रिंग को पार्स करना समाप्त कर दिया, मेल खाते हुए नेस्टेड {.*?}
समूहों (एक find_all()
सहायक फ़ंक्शन का उपयोग करके ) और किसी भी क्षमता को पकड़ने के दौरान उत्तरोत्तर स्ट्रिंग का निर्माण प्रगतिशील रूप से और सीधे उपयोग str.format_map()
करते हुए किया KeyError
।
def find_all(
text,
pattern,
overlap=False):
"""
Find all occurrencies of the pattern in the text.
Args:
text (str|bytes|bytearray): The input text.
pattern (str|bytes|bytearray): The pattern to find.
overlap (bool): Detect overlapping patterns.
Yields:
position (int): The position of the next finding.
"""
len_text = len(text)
offset = 1 if overlap else (len(pattern) or 1)
i = 0
while i < len_text:
i = text.find(pattern, i)
if i >= 0:
yield i
i += offset
else:
break
def matching_delimiters(
text,
l_delim,
r_delim,
including=True):
"""
Find matching delimiters in a sequence.
The delimiters are matched according to nesting level.
Args:
text (str|bytes|bytearray): The input text.
l_delim (str|bytes|bytearray): The left delimiter.
r_delim (str|bytes|bytearray): The right delimiter.
including (bool): Include delimeters.
yields:
result (tuple[int]): The matching delimiters.
"""
l_offset = len(l_delim) if including else 0
r_offset = len(r_delim) if including else 0
stack = []
l_tokens = set(find_all(text, l_delim))
r_tokens = set(find_all(text, r_delim))
positions = l_tokens.union(r_tokens)
for pos in sorted(positions):
if pos in l_tokens:
stack.append(pos + 1)
elif pos in r_tokens:
if len(stack) > 0:
prev = stack.pop()
yield (prev - l_offset, pos + r_offset, len(stack))
else:
raise ValueError(
'Found `{}` unmatched right token(s) `{}` (position: {}).'
.format(len(r_tokens) - len(l_tokens), r_delim, pos))
if len(stack) > 0:
raise ValueError(
'Found `{}` unmatched left token(s) `{}` (position: {}).'
.format(
len(l_tokens) - len(r_tokens), l_delim, stack.pop() - 1))
def safe_format_map(
text,
source):
"""
Perform safe string formatting from a mapping source.
If a value is missing from source, this is simply ignored, and no
`KeyError` is raised.
Args:
text (str): Text to format.
source (Mapping|None): The mapping to use as source.
If None, uses caller's `vars()`.
Returns:
result (str): The formatted text.
"""
stack = []
for i, j, depth in matching_delimiters(text, '{', '}'):
if depth == 0:
try:
replacing = text[i:j].format_map(source)
except KeyError:
pass
else:
stack.append((i, j, replacing))
result = ''
i, j = len(text), 0
while len(stack) > 0:
last_i = i
i, j, replacing = stack.pop()
result = replacing + text[j:last_i] + result
if i > 0:
result = text[0:i] + result
return result
(यह कोड फ्लाइंगक्रीकस - DISCLAIMER में भी उपलब्ध है : मैं इसका मुख्य लेखक हूं।)
इस कोड का उपयोग होगा:
print(safe_format_map('{a} {b} {c}', dict(a=-A-)))
# -A- {b} {c}
आइए इसकी तुलना मेरे पसंदीदा समाधान से करें (@SvenMarnach द्वारा कृपया अपना कोड यहां और वहां साझा किया ):
import string
class FormatPlaceholder:
def __init__(self, key):
self.key = key
def __format__(self, spec):
result = self.key
if spec:
result += ":" + spec
return "{" + result + "}"
def __getitem__(self, index):
self.key = "{}[{}]".format(self.key, index)
return self
def __getattr__(self, attr):
self.key = "{}.{}".format(self.key, attr)
return self
class FormatDict(dict):
def __missing__(self, key):
return FormatPlaceholder(key)
def safe_format_alt(text, source):
formatter = string.Formatter()
return formatter.vformat(text, (), FormatDict(source))
यहाँ कुछ परीक्षण हैं:
test_texts = (
'{b} {f}', # simple nothing useful in source
'{a} {b}', # simple
'{a} {b} {c:5d}', # formatting
'{a} {b} {c!s}', # coercion
'{a} {b} {c!s:>{a}s}', # formatting and coercion
'{a} {b} {c:0{a}d}', # nesting
'{a} {b} {d[x]}', # dicts (existing in source)
'{a} {b} {e.index}', # class (existing in source)
'{a} {b} {f[g]}', # dict (not existing in source)
'{a} {b} {f.values}', # class (not existing in source)
)
source = dict(a=4, c=101, d=dict(x='FOO'), e=[])
और इसे चलाने के लिए कोड:
funcs = safe_format_map, safe_format_alt
n = 18
for text in test_texts:
full_source = {**dict(b='---', f=dict(g='Oh yes!')), **source}
print('{:>{n}s} : OK : '.format('str.format_map', n=n) + text.format_map(full_source))
for func in funcs:
try:
print(f'{func.__name__:>{n}s} : OK : ' + func(text, source))
except:
print(f'{func.__name__:>{n}s} : FAILED : {text}')
जिसके परिणामस्वरूप:
str.format_map : OK : --- {'g': 'Oh yes!'}
safe_format_map : OK : {b} {f}
safe_format_alt : OK : {b} {f}
str.format_map : OK : 4 ---
safe_format_map : OK : 4 {b}
safe_format_alt : OK : 4 {b}
str.format_map : OK : 4 --- 101
safe_format_map : OK : 4 {b} 101
safe_format_alt : OK : 4 {b} 101
str.format_map : OK : 4 --- 101
safe_format_map : OK : 4 {b} 101
safe_format_alt : OK : 4 {b} 101
str.format_map : OK : 4 --- 101
safe_format_map : OK : 4 {b} 101
safe_format_alt : OK : 4 {b} 101
str.format_map : OK : 4 --- 0101
safe_format_map : OK : 4 {b} 0101
safe_format_alt : OK : 4 {b} 0101
str.format_map : OK : 4 --- FOO
safe_format_map : OK : 4 {b} FOO
safe_format_alt : OK : 4 {b} FOO
str.format_map : OK : 4 --- <built-in method index of list object at 0x7f7a485666c8>
safe_format_map : OK : 4 {b} <built-in method index of list object at 0x7f7a485666c8>
safe_format_alt : OK : 4 {b} <built-in method index of list object at 0x7f7a485666c8>
str.format_map : OK : 4 --- Oh yes!
safe_format_map : OK : 4 {b} {f[g]}
safe_format_alt : OK : 4 {b} {f[g]}
str.format_map : OK : 4 --- <built-in method values of dict object at 0x7f7a485da090>
safe_format_map : OK : 4 {b} {f.values}
safe_format_alt : OK : 4 {b} {f.values}
जैसा कि आप देख सकते हैं, अपडेट किया गया संस्करण अब कोने के मामलों को अच्छी तरह से संभालता है जहां पहले का संस्करण विफल हो जाता था।
समय, वे लगभग भीतर हैं। एक-दूसरे का 50%, वास्तविक text
स्वरूप पर निर्भर करता है (और वास्तविक होने की संभावना है source
), लेकिन safe_format_map()
लगता है कि मेरे द्वारा किए गए अधिकांश परीक्षणों में एक धार है (जो भी उनका मतलब है, निश्चित रूप से):
for text in test_texts:
print(f' {text}')
%timeit safe_format(text * 1000, source)
%timeit safe_format_alt(text * 1000, source)
{b} {f}
3.93 ms ± 153 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
6.35 ms ± 51.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
{a} {b}
4.37 ms ± 57.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
5.2 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
{a} {b} {c:5d}
7.15 ms ± 91.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
7.76 ms ± 69.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
{a} {b} {c!s}
7.04 ms ± 138 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
7.56 ms ± 161 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
{a} {b} {c!s:>{a}s}
8.91 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
10.5 ms ± 181 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
{a} {b} {c:0{a}d}
8.84 ms ± 147 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
10.2 ms ± 202 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
{a} {b} {d[x]}
7.01 ms ± 197 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
7.35 ms ± 106 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
{a} {b} {e.index}
11 ms ± 68.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
8.78 ms ± 405 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
{a} {b} {f[g]}
6.55 ms ± 88.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
9.12 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
{a} {b} {f.values}
6.61 ms ± 55.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
9.92 ms ± 98.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
{d[x]}
जहां तक मुझे पता है, एक मान्य प्रारूप स्ट्रिंग नहीं है।
field_name ::= arg_name ("." attribute_name | "[" element_index "]")*
और दोनों str.format()
और str.format_map()
यह समझते हैं। मैं कहूंगा कि इसके लिए एक वैध प्रारूप स्ट्रिंग होने के लिए पर्याप्त सबूत हैं।
str.format()
वर्ग कोष्ठक में गैर-पूर्णांक सूचकांक के साथ उपयोग करने का एक उदाहरण दे सकते हैं ? मैं केवल पूर्णांक अनुक्रमित कार्य कर सकता हूं।
a = dict(b='YAY!'); '{a[b]}'.format_map(dict(a=a))
आपको ' YAY !'
a[b]
पायथन कोड की तरह समझा जाता है , लेकिन यह वास्तव में a["b"]
धन्यवाद है!
यदि आप इस से संबंधित प्रश्न के रूप मेंformat
, तर्कों को पास करने के लिए एक शब्दकोश अनपैक करना चाहते हैं , तो आप निम्न विधि का उपयोग कर सकते हैं।
पहले मान लें कि s
इस प्रश्न में स्ट्रिंग समान है:
s = '{foo} {bar}'
और मान निम्नलिखित शब्दकोश द्वारा दिए गए हैं:
replacements = {'foo': 'FOO'}
स्पष्ट रूप से यह काम नहीं करेगा:
s.format(**replacements)
#---------------------------------------------------------------------------
#KeyError Traceback (most recent call last)
#<ipython-input-29-ef5e51de79bf> in <module>()
#----> 1 s.format(**replacements)
#
#KeyError: 'bar'
हालाँकि, आप पहले सभी नामित दलीलों में से एक प्राप्तset
s
कर सकते हैं और एक शब्दकोश बना सकते हैं जो तर्क को खुद को घुंघराले ब्रेसिज़ में लपेटता है:
from string import Formatter
args = {x[1]:'{'+x[1]+'}' for x in Formatter().parse(s)}
print(args)
#{'foo': '{foo}', 'bar': '{bar}'}
अब args
अनुपलब्ध कुंजियों को भरने के लिए शब्दकोश का उपयोग करें replacements
। अजगर 3.5+ के लिए, आप इसे एकल अभिव्यक्ति में कर सकते हैं :
new_s = s.format(**{**args, **replacements}}
print(new_s)
#'FOO {bar}'
अजगर के पुराने संस्करणों के लिए, आप कॉल कर सकते हैं update
:
args.update(replacements)
print(s.format(**args))
#'FOO {bar}'
मुझे @ sven-marnach जवाब पसंद है। मेरा जवाब बस इसका एक विस्तारित संस्करण है। यह गैर-कीवर्ड फ़ॉर्मेटिंग की अनुमति देता है और अतिरिक्त कुंजियों को अनदेखा करता है। यहां उपयोग के उदाहरण दिए गए हैं (एक फ़ंक्शन का नाम अजगर 3.6 एफ-स्ट्रिंग प्रारूपण के लिए एक संदर्भ है):
# partial string substitution by keyword
>>> f('{foo} {bar}', foo="FOO")
'FOO {bar}'
# partial string substitution by argument
>>> f('{} {bar}', 1)
'1 {bar}'
>>> f('{foo} {}', 1)
'{foo} 1'
# partial string substitution with arguments and keyword mixed
>>> f('{foo} {} {bar} {}', '|', bar='BAR')
'{foo} | BAR {}'
# partial string substitution with extra keyword
>>> f('{foo} {bar}', foo="FOO", bro="BRO")
'FOO {bar}'
# you can simply 'pour out' your dictionary to format function
>>> kwargs = {'foo': 'FOO', 'bro': 'BRO'}
>>> f('{foo} {bar}', **kwargs)
'FOO {bar}'
और यहाँ मेरा कोड है:
from string import Formatter
class FormatTuple(tuple):
def __getitem__(self, key):
if key + 1 > len(self):
return "{}"
return tuple.__getitem__(self, key)
class FormatDict(dict):
def __missing__(self, key):
return "{" + key + "}"
def f(string, *args, **kwargs):
"""
String safe substitute format method.
If you pass extra keys they will be ignored.
If you pass incomplete substitute map, missing keys will be left unchanged.
:param string:
:param kwargs:
:return:
>>> f('{foo} {bar}', foo="FOO")
'FOO {bar}'
>>> f('{} {bar}', 1)
'1 {bar}'
>>> f('{foo} {}', 1)
'{foo} 1'
>>> f('{foo} {} {bar} {}', '|', bar='BAR')
'{foo} | BAR {}'
>>> f('{foo} {bar}', foo="FOO", bro="BRO")
'FOO {bar}'
"""
formatter = Formatter()
args_mapping = FormatTuple(args)
mapping = FormatDict(kwargs)
return formatter.vformat(string, args_mapping, mapping)
@Sam बॉर्न की टिप्पणी को पढ़कर, मैंने कस्टम पार्सर लिखे बिना
जबरदस्ती (जैसे ) काम करने के लिए @ SvenMarnach के कोड को संशोधित किया {a!s:>2s}
। मूल विचार स्ट्रिंग्स में कनवर्ट नहीं करना है, लेकिन गायब होने वाली चाबियों को जबरदस्ती टैग के साथ जोड़ना है।
import string
class MissingKey(object):
def __init__(self, key):
self.key = key
def __str__(self): # Supports {key!s}
return MissingKeyStr("".join([self.key, "!s"]))
def __repr__(self): # Supports {key!r}
return MissingKeyStr("".join([self.key, "!r"]))
def __format__(self, spec): # Supports {key:spec}
if spec:
return "".join(["{", self.key, ":", spec, "}"])
return "".join(["{", self.key, "}"])
def __getitem__(self, i): # Supports {key[i]}
return MissingKey("".join([self.key, "[", str(i), "]"]))
def __getattr__(self, name): # Supports {key.name}
return MissingKey("".join([self.key, ".", name]))
class MissingKeyStr(MissingKey, str):
def __init__(self, key):
if isinstance(key, MissingKey):
self.key = "".join([key.key, "!s"])
else:
self.key = key
class SafeFormatter(string.Formatter):
def __init__(self, default=lambda k: MissingKey(k)):
self.default=default
def get_value(self, key, args, kwds):
if isinstance(key, str):
return kwds.get(key, self.default(key))
else:
return super().get_value(key, args, kwds)
इस तरह (उदाहरण के लिए) का उपयोग करें
SafeFormatter().format("{a:<5} {b:<10}", a=10)
निम्नलिखित परीक्षण (@ नोक 2 से परीक्षणों से प्रेरित) पारंपरिक format_map
और safe_format_map
दो मामलों में ऊपर दिए गए वर्ग के आधार पर आउटपुट की जांच करते हैं : सही कीवर्ड प्रदान करते हैं या उनके बिना।
def safe_format_map(text, source):
return SafeFormatter().format(text, **source)
test_texts = (
'{a} ', # simple nothing useful in source
'{a:5d}', # formatting
'{a!s}', # coercion
'{a!s:>{a}s}', # formatting and coercion
'{a:0{a}d}', # nesting
'{d[x]}', # indexing
'{d.values}', # member
)
source = dict(a=10,d=dict(x='FOO'))
funcs = [safe_format_map,
str.format_map
#safe_format_alt # Version based on parsing (See @norok2)
]
n = 18
for text in test_texts:
# full_source = {**dict(b='---', f=dict(g='Oh yes!')), **source}
# print('{:>{n}s} : OK : '.format('str.format_map', n=n) + text.format_map(full_source))
print("Testing:", text)
for func in funcs:
try:
print(f'{func.__name__:>{n}s} : OK\t\t\t: ' + func(text, dict()))
except:
print(f'{func.__name__:>{n}s} : FAILED')
try:
print(f'{func.__name__:>{n}s} : OK\t\t\t: ' + func(text, source))
except:
print(f'{func.__name__:>{n}s} : FAILED')
जो आउटपुट देता है
Testing: {a}
safe_format_map : OK : {a}
safe_format_map : OK : 10
format_map : FAILED
format_map : OK : 10
Testing: {a:5d}
safe_format_map : OK : {a:5d}
safe_format_map : OK : 10
format_map : FAILED
format_map : OK : 10
Testing: {a!s}
safe_format_map : OK : {a!s}
safe_format_map : OK : 10
format_map : FAILED
format_map : OK : 10
Testing: {a!s:>{a}s}
safe_format_map : OK : {a!s:>{a}s}
safe_format_map : OK : 10
format_map : FAILED
format_map : OK : 10
Testing: {a:0{a}d}
safe_format_map : OK : {a:0{a}d}
safe_format_map : OK : 0000000010
format_map : FAILED
format_map : OK : 0000000010
Testing: {d[x]}
safe_format_map : OK : {d[x]}
safe_format_map : OK : FOO
format_map : FAILED
format_map : OK : FOO
Testing: {d.values}
safe_format_map : OK : {d.values}
safe_format_map : OK : <built-in method values of dict object at 0x7fe61e230af8>
format_map : FAILED
format_map : OK : <built-in method values of dict object at 0x7fe61e230af8>
आप इसे ऐसे फ़ंक्शन में लपेट सकते हैं जो डिफ़ॉल्ट तर्क लेता है:
def print_foo_bar(foo='', bar=''):
s = '{foo} {bar}'
return s.format(foo=foo, bar=bar)
print_foo_bar(bar='BAR') # ' BAR'
{bar:1.2f}