python форматтағы пайдаланылмаған аталмыш дәлелдер

Айталық, менде бар:

action = '{bond}, {james} {bond}'.format(bond='bond', james='james')

бұл нәтиже:

'bond, james bond' 

Бұдан кейін бізде:

 action = '{bond}, {james} {bond}'.format(bond='bond')

бұл:

KeyError: 'james'

Бұл қателіктің пайда болуын болдырмау үшін кейбір шешімдер бар ма?

  • егер keyrror: ignore, жалғыз қалдырыңыз (бірақ басқаларды талдаңыз)
  • Пішім жолын қол жетімді аргументтермен салыстырыңыз, егер жоқ болса,
  • қосыңыз
38
Екіншіден, менің ойымша жақсы. Біріншісі - ғажайып контент жасауы мүмкін. Екіншіден, адамдар «бұл жерде бір нәрсе дұрыс емес» деп ойлайды, бұл жақсы жағдайда
қосылды автор nelsonvarela, көзі
қосылды автор dreftymac, көзі
bond, bond / bond, {james}, bond дегенді қалайсыз?
қосылды автор falsetru, көзі
Мен екі жағдайда да жауапты жаңарттым.
қосылды автор falsetru, көзі
python жолында литеральный бұйра таңбаларды қалай басып шығара аламын және «> stackoverflow.com/questions/5466451/…»
қосылды автор Qlimax, көзі

9 жауаптар

Егер Python 3.2+ пайдалансаңыз, str.format_map() қолдануға болады.

bond, bond үшін:

>>> from collections import defaultdict
>>> '{bond}, {james} {bond}'.format_map(defaultdict(str, bond='bond'))
'bond,  bond'

bond, {james} bond үшін:

>>> class SafeDict(dict):
...     def __missing__(self, key):
...         return '{' + key + '}'
...
>>> '{bond}, {james} {bond}'.format_map(SafeDict(bond='bond'))
'bond, {james} bond'

В Python 2.6/2.7

bond, bond үшін:

>>> from collections import defaultdict
>>> import string
>>> string.Formatter().vformat('{bond}, {james} {bond}', (), defaultdict(str, bond='bond'))
'bond,  bond'

bond, {james} bond үшін:

>>> from collections import defaultdict
>>> import string
>>>
>>> class SafeDict(dict):
...     def __missing__(self, key):
...         return '{' + key + '}'
...
>>> string.Formatter().vformat('{bond}, {james} {bond}', (), SafeDict(bond='bond'))
'bond, {james} bond'
60
қосылды
Мен 2.7 пайдаланамын .. рахмет! Мен мұны тексеремін
қосылды автор nelsonvarela, көзі
Тамаша! Қабылданды!
қосылды автор nelsonvarela, көзі
Қиындық жеткіліксіз, python3 форматында осы мысалда бірдей нәтиже береді format_map .
қосылды автор frnhr, көзі
Бұл жерде '{bond}, {james} {bond} форматында (** SecurityDict (bond =' bond ')) қайтарады ' bond, {james} bond . ** ескеріңіз. Мен оны әлі күнге дейін байқамайды :) Python 3.4.3
қосылды автор frnhr, көзі
@frnhr, сіз тырысты нақты мысал көрсетіңіз?
қосылды автор falsetru, көзі
@frnhr, мен көремін. Пікіріңізге рахмет.
қосылды автор falsetru, көзі
@frnhr, бұл нені білдіреді? '{bond}, {james} {bond} форматында (defaultdict (str, bond =' bond ')) маған KeyError береді.
қосылды автор falsetru, көзі

safe_substitute әдісімен шаблонның жолын пайдалануға болады.

from string import Template

tpl = Template('$bond, $james $bond')
action = tpl.safe_substitute({'bond': 'bond'})
17
қосылды

Сіз ұсыныстарды PEP 3101 және субкласс форматоры арқылы орындауға болады:

from __future__ import print_function
import string

class MyFormatter(string.Formatter):
    def __init__(self, default='{{{0}}}'):
        self.default=default

    def get_value(self, key, args, kwds):
        if isinstance(key, str):
            return kwds.get(key, self.default.format(key))
        else:
            Formatter.get_value(key, args, kwds)

Енді көріңіз:

>>> fmt=MyFormatter()
>>> fmt.format("{bond}, {james} {bond}", bond='bond', james='james')
'bond, james bond'
>>> fmt.format("{bond}, {james} {bond}", bond='bond')
'bond, {james} bond'

self.default ішіндегі мәтінді KeyErrors үшін көрсету керек дегенге өзгерту арқылы негізгі қателер қалай белгіленетінін өзгертуге болады:

>>> fmt=MyFormatter('">>{{{0}}} KeyError<<"')
>>> fmt.format("{bond}, {james} {bond}", bond='bond', james='james')
'bond, james bond'
>>> fmt.format("{bond}, {james} {bond}", bond='bond')
'bond, ">>{james} KeyError<<" bond'

Коды Python 2.6, 2.7 және 3.0+ деңгейінде өзгеріссіз жұмыс істейді

9
қосылды
Formatter.get_value (key, args, kwds) return string.Formatter.get_value (self, key, args, kwds) сіздің кодыңызда
қосылды автор Grijesh Chauhan, көзі
Берілген жауаптардың ішінен портреттік/талғампаздық +1 тұрғысынан бұл ең жақсы деп ойлаймын
қосылды автор Ajay, көзі
@GrijeshChauhan Мен сенімдімін ... Бұл функция рекурсивно деп аталады, соңғы, терминалда жалғыз нақты қайтару орын алады ... Қалай болғанда да, оны жұмыс істей алмадым. Мен қарапайым кодпен аяқтадым. класс URLFormatter (string.Formatter): def __init __ (self, default = '{}'): self.default = әдепкі def get_value (self, key, args, kwds): return kwds.get (key, self.default.format (кілт))
қосылды автор Stéphane, көзі

Сондай-ақ қарапайым және оқуға болады, бірақ бірнеше глупо:

'{bond}, {james} {bond}'.format(bond='bond', james='{james}')

Бұл жауап күтілген кілттерді білуді талап ететінін білемін, бірақ қарапайым екі қадамды ауыстыруды іздедім (айталық, алдымен проблема атауы, содан кейін цикл ішіндегі проблема индексі) және барлық сыныпты немесе оқылмайтын кодты жасау қажет болғанда күрделі болды.

6
қосылды

falsetru's answer has a clever use of a defaulting dictionary with vformat(), and dawg's answer is perhaps more in-line with Python's documentation, but neither handle compound field names (e.g., with explicit conversion (!r) or format specs (:+10g).

Мысалы, falsetru's SafeDict пайдалану:

>>> string.Formatter().vformat('{one} {one:x} {one:10f} {two!r} {two[0]}', (), SafeDict(one=215, two=['James', 'Bond']))
"215 d7 215.000000 ['James', 'Bond'] James"
>>> string.Formatter().vformat('{one} {one:x} {one:10f} {two!r} {two[0]}', (), SafeDict(one=215))
"215 d7 215.000000 '{two}' {"

Dawg's MyFormatter бағдарламасын пайдалану:

>>> MyFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}', one=215, two=['James', 'Bond'])
"215 d7 215.000000 ['James', 'Bond'] James"
>>> MyFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}', one=215)
"215 d7 215.000000 '{two}' {"

Екінші жағдайда да жақсы жұмыс істемейді, себебі мәнді іздеу ( get_value() )) форматтау сипаттамаларын әлдеқашан тастаған. Оның орнына, бұл сипаттамалар қол жетімді болуы үшін vformat() немесе parse() дегенді қайта анықтай аласыз. Төменде келтірілген шешімді, ол кілт іздеуді жүзеге асыратындықтан, vformat() параметрін қайта анықтау арқылы жасалады және егер кілт жоқ болса, екі жақты жақшалармен (мысалы, {{two! R}} ) және қалыпты vformat() орындайды.

class SafeFormatter(string.Formatter):
    def vformat(self, format_string, args, kwargs):
        args_len = len(args)  # for checking IndexError
        tokens = []
        for (lit, name, spec, conv) in self.parse(format_string):
            # re-escape braces that parse() unescaped
            lit = lit.replace('{', '{{').replace('}', '}}')
            # only lit is non-None at the end of the string
            if name is None:
                tokens.append(lit)
            else:
                # but conv and spec are None if unused
                conv = '!' + conv if conv else ''
                spec = ':' + spec if spec else ''
                # name includes indexing ([blah]) and attributes (.blah)
                # so get just the first part
                fp = name.split('[')[0].split('.')[0]
                # treat as normal if fp is empty (an implicit
                # positional arg), a digit (an explicit positional
                # arg) or if it is in kwargs
                if not fp or fp.isdigit() or fp in kwargs:
                    tokens.extend([lit, '{', name, conv, spec, '}'])
                # otherwise escape the braces
                else:
                    tokens.extend([lit, '{{', name, conv, spec, '}}'])
        format_string = ''.join(tokens)  # put the string back together
        # finally call the default formatter
        return string.Formatter.vformat(self, format_string, args, kwargs)

Міне, ол:

>>> SafeFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}', one=215, two=['James', 'Bond'])
"215 d7 215.000000 ['James', 'Bond'] James"
>>> SafeFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}', one=215)
'215 d7 215.000000 {two!r} {two[0]}'
>>> SafeFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}')
'{one} {one:x} {one:10f} {two!r} {two[0]}'
>>> SafeFormatter().format('{one} {one:x} {one:10f} {two!r} {two[0]}', two=['James', 'Bond'])
"{one} {one:x} {one:10f} ['James', 'Bond'] James"

Бұл шешім біраз сәтсіз (мүмкін, parse() ) қайтадан пирамидалар болуы мүмкін), бірақ одан да көп пішімдеу жолдары үшін жұмыс істеу керек.

5
қосылды

Міне, python27 арқылы мұны істеудің тағы бір жолы:

action = '{bond}, {james} {bond}'
d = dict((x[1], '') for x in action._formatter_parser())
# Now we have: `d = {'james': '', 'bond': ''}`.
d.update(bond='bond')
print action.format(**d)  # bond,  bond
2
қосылды
Кейбіреулері _formatter_parser қолданған кезде жұлынуы мүмкін, бірақ маған бұл ең питон әдісі: қарапайым, оңай түсіну, қораптан тыс функционалдылықты пайдаланады және екінші жолды d = dict (x [1], '{' + str (x [1]) + '}') x ішінде action._formatter_parser ()) bond, bond форматындағыдай оңай болады.
қосылды автор hlongmore, көзі

Пішімдік жолдарды ішінара толтыру керек, мысалы, пішімдегі жолдарды бірте-бірте толтырған кездегі жалпы мәселе. SQL сұраулары үшін.

format_partial() method uses the Formatter from string and ast to parse the format string and also find out whether the named parameter hash has all the values needed to partially evaluate the format:

import ast
from collections import defaultdict
from itertools import chain, ifilter, imap
from operator import itemgetter
import re
from string import Formatter

def format_partial(fstr, **kwargs):
    def can_resolve(expr, **kwargs):
        walk = chain.from_iterable(imap(ast.iter_fields, ast.walk(ast.parse(expr))))
        return all(v in kwargs for k,v in ifilter(lambda (k,v): k=='id', walk))

    ostr = fstr
    fmtr = Formatter()
    dd = defaultdict(int)
    fmtr.get_field = lambda field_name, args, kwargs: (dd[field_name],field_name)
    fmtr.check_unused_args = lambda used_args, args, kwargs: all(v in dd for v in used_args)
    for t in ifilter(itemgetter(1), Formatter().parse(fstr)):
        f = '{'+t[1]+(':'+t[2] if t[2] else '')+'}'
        dd = defaultdict(int)
        fmtr.format(f,**kwargs)
        if all(can_resolve(e,**kwargs) for e in dd):
            ostr = re.sub(re.escape(f),Formatter().format(f, **kwargs),ostr,count=1)
    return ostr

format_partial will leave the unresolved portion of the format string, so subsequent calls can be used to resolve those parts as the data is available.

goodmami's and dawg's answers seem cleaner, but they both fail to capture the format mini-language completely as in {x:>{x}}; format_partial will have no problem resolving any format string that string.format() resolves:

from datetime import date
format_partial('{x} {} {y[1]:x} {x:>{x}} {z.year}', **{'x':30, 'y':[1,2], 'z':date.today()})

'30 {} 2                             30 2016'

Ескі функцияны ескі стиль пішіміндегі жолдарға кеңейтіп, ескі стиль пішіміндегі подстрокам (яғни кірістірілген маркерлер) қалыпты болғандықтан, жол пішімдеушінің орнына regex түрін қолдануға болады.

1
қосылды
Кодтың шешілген пішіндерді алмастыратын пішін үлгілері арқылы өтетін жолы - үлкен багги.
қосылды автор topkara, көзі

Python 3 үшін, бекітілген жауапты қабылдаған кезде, бұл жақсы, тығыз Pythonic іске асыру:

def safeformat(str, **kwargs):
    class SafeDict(dict):
        def __missing__(self, key):
            return '{' + key + '}'
    replacements = SafeDict(**kwargs)
    return str.format_map(replacements)

# In [1]: safeformat("a: {a}, b: {b}, c: {c}", a="A", c="C", d="D")
# Out[1]: 'a: A, b: {b}, c: C'
0
қосылды
Өкінішке орай, бұл «{a: <10}» жолының жағдайын реттемейді.
қосылды автор Marcel Wilson, көзі

Based on some of the other answers, I expanded the solutions. This will handle strings with formatting spec "{a:<10}".

Мен selenium тіркеуден алынған кейбір жолдар vformat (және format_map) рекурсия шегіне жетуіне себеп болды. Сондай-ақ, бос бұйырылған қиғаштар бар жердегі жолдармен жұмыс істей алатынымды қамтамасыз етуді қаладым.

def partialformat(s: str, recursionlimit: int = 10, **kwargs):
    """
    vformat does the acutal work of formatting strings. _vformat is the 
    internal call to vformat and has the ability to alter the recursion 
    limit of how many embedded curly braces to handle. But for some reason 
    vformat does not.  vformat also sets the limit to 2!   

    The 2nd argument of _vformat 'args' allows us to pass in a string which 
    contains an empty curly brace set and ignore them.
    """

    class FormatPlaceholder:
        def __init__(self, key):
            self.key = key

        def __format__(self, spec):
            result = self.key
            if spec:
                result += ":" + spec
            return "{" + result + "}"

    class FormatDict(dict):
        def __missing__(self, key):
            return FormatPlaceholder(key)

    class PartialFormatter(string.Formatter):
        def get_field(self, field_name, args, kwargs):
            try:
                obj, first = super(PartialFormatter, self).get_field(field_name, args, kwargs)
            except (IndexError, KeyError, AttributeError):
                first, rest = formatter_field_name_split(field_name)
                obj = '{' + field_name + '}'

                # loop through the rest of the field_name, doing
                #  getattr or getitem as needed
                for is_attr, i in rest:
                    if is_attr:
                        try:
                            obj = getattr(obj, i)
                        except AttributeError as exc:
                            pass
                    else:
                        obj = obj[i]

            return obj, first

    fmttr = string.Formatter()
    fs, _ = fmttr._vformat(s, ("{}",), FormatDict(**kwargs), set(), recursionlimit)
    return fs

class ColorObj(object):
    blue = "^BLUE^"
s = '{"a": {"b": {"c": {"d" : {} {foo:<12} & {foo!r} {arg} {color.blue:<10} {color.pink} {blah.atr} }}}}'
print(partialformat(s, foo="Fooolery", arg="ARRrrrrrg!", color=ColorObj))

шығу:

{"a": {"b": {"c": {"d" : {} Fooolery             & 'Fooolery' Fooolery ARRrrrrrg! ^BLUE^ {color.pink} {blah.atr} }}}}
0
қосылды