tablas - hacer un dataframe en python
serie de formato Python argumentos no utilizados con nombre (9)
Digamos que tengo:
action = ''{bond}, {james} {bond}''.format(bond=''bond'', james=''james'')
esta salida será:
''bond, james bond''
A continuación tenemos:
action = ''{bond}, {james} {bond}''.format(bond=''bond'')
esto dará como resultado:
KeyError: ''james''
¿Hay alguna solución para evitar que ocurra este error, algo como:
- si keyrror: ignora, déjalo en paz (pero analiza a otros)
- compare la cadena de formato con los argumentos con nombre disponibles, si falta, agregue
Si está usando Python 3.2+, use puede usar str.format_map() .
Para bond, bond
>>> from collections import defaultdict
>>> ''{bond}, {james} {bond}''.format_map(defaultdict(str, bond=''bond''))
''bond, bond''
Para bond, {james} bond
:
>>> class SafeDict(dict):
... def __missing__(self, key):
... return ''{'' + key + ''}''
...
>>> ''{bond}, {james} {bond}''.format_map(SafeDict(bond=''bond''))
''bond, {james} bond''
En Python 2.6 / 2.7
Para bond, bond
>>> from collections import defaultdict
>>> import string
>>> string.Formatter().vformat(''{bond}, {james} {bond}'', (), defaultdict(str, bond=''bond''))
''bond, bond''
Para 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''
Aquí hay otra forma de hacerlo usando 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
Basándome en algunas de las otras respuestas, amplié las soluciones. Esto manejará cadenas con la especificación de formato "{a:<10}"
.
Descubrí que algunas cadenas del registro de selenio causaban que vformat (y format_map) alcanzaran un límite de recursión. También quería asegurarme de poder manejar cuerdas donde también existan llaves vacías.
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))
salida:
{"a": {"b": {"c": {"d" : {} Fooolery & ''Fooolery'' Fooolery ARRrrrrrg! ^BLUE^ {color.pink} {blah.atr} }}}}
La necesidad de completar parcialmente las cadenas de formato es un problema común cuando se rellenan progresivamente las cadenas de formato, por ejemplo, para consultas SQL.
format_partial()
método format_partial()
utiliza el Formatter
desde la string
y el ast
para analizar la cadena del formato y también para averiguar si el hash del parámetro nombrado tiene todos los valores necesarios para evaluar parcialmente el formato:
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
dejará la parte no resuelta de la cadena de formato, por lo que las llamadas subsiguientes se pueden usar para resolver esas partes cuando los datos estén disponibles.
Las respuestas de goodmami y dawg parecen más limpias, pero ambas fallan en capturar el formato en mini-lenguaje completamente como en {x:>{x}}
; format_partial
no tendrá problemas para resolver cualquier cadena de formato que string.format()
:
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''
Es incluso más fácil extender la funcionalidad a las cadenas de formato de estilo antiguo usando expresiones regulares en lugar del formateador de cadena, ya que las subcadenas de formato de estilo antiguo eran regulares (es decir, no hay marcadores anidados).
Para Python 3, tomando la respuesta aprobada, esta es una implementación Pythonic agradable y ajustada:
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''
Podría usar una cadena de plantilla con el método safe_substitute
.
from string import Template
tpl = Template(''$bond, $james $bond'')
action = tpl.safe_substitute({''bond'': ''bond''})
Puedes seguir la recomendación en PEP 3101 y subclase Formatter:
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:
return string.Formatter.get_value(key, args, kwds)
Ahora inténtalo:
>>> fmt=MyFormatter()
>>> fmt.format("{bond}, {james} {bond}", bond=''bond'', james=''james'')
''bond, james bond''
>>> fmt.format("{bond}, {james} {bond}", bond=''bond'')
''bond, {james} bond''
Puede cambiar cómo se marcan los errores clave cambiando el texto en self.default
a lo que le gustaría mostrar para 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''
El código funciona sin cambios en Python 2.6, 2.7 y 3.0+
También se puede hacer lo simple y lo legible, aunque algo tonto:
''{bond}, {james} {bond}''.format(bond=''bond'', james=''{james}'')
Sé que esta respuesta requiere el conocimiento de las claves esperadas, pero estaba buscando una simple sustitución de dos pasos (primero el nombre del problema, luego el índice del problema dentro de un bucle) y crear una clase completa o un código ilegible era más complejo de lo necesario.
la respuesta de falsetru tiene un uso inteligente de un diccionario vformat()
con vformat()
, y la respuesta de dawg es quizás más en línea con la documentación de Python, pero ninguno maneja nombres de campos compuestos (por ejemplo, con conversión explícita ( !r
) o especificaciones de formato ( :+10g
).
Por ejemplo, utilizando SafeDict de falsetru:
>>> 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}'' {"
Y usando MyFormatter de dawg:
>>> 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}'' {"
Ninguno funciona bien en el segundo caso porque el valor de búsqueda (en get_value()
) ya ha eliminado las especificaciones de formato. En su lugar, puede redefinir vformat()
o parse()
para que estas especificaciones estén disponibles. Mi solución a continuación hace esto al redefinir vformat()
para que realice la búsqueda de claves y, si falta la clave, escapa la cadena de formato con llaves dobles (por ejemplo, {{two!r}}
) y luego ejecuta el vformat()
normal 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)
Aquí está en acción:
>>> 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"
Esta solución es un poco demasiado complicada (tal vez la redefinición de parse()
tendría menos kludges), pero debería funcionar para más cadenas de formato.