Keyerror in multiple key string interpolation in Python - python

I am having a problem like
In [5]: x = "this string takes two like {one} and {two}"
In [6]: y = x.format(one="one")
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-6-b3c89fbea4d3> in <module>()
----> 1 y = x.format(one="one")
KeyError: 'two'
I have a compound string with many keys that gets kept in a config file. For 8 different queries, they all use the same string, except 1 key is a different setting. I need to be able to substitute a key in that file to save the strings for later like:
"this string takes two like one and {two}"
How do I substitute one key at a time using format?

I think string.Template does what you want:
from string import Template
s = "this string takes two like $one and $two"
s = Template(s).safe_substitute(one=1)
print(s)
# this string takes two like 1 and $two
s = Template(s).safe_substitute(two=2)
print(s)
# this string takes two like 1 and 2

If placeholders in your string don't have any format specifications, in Python 3 you can use str.format_map and provide a mapping, returning the field name for missing fields:
class Default(dict):
def __missing__(self, key):
return '{' + key + '}'
In [6]: x = "this string takes two like {one} and {two}"
In [7]: x.format_map(Default(one=1))
Out[7]: 'this string takes two like 1 and {two}'
If you do have format specifications, you'll have to subclass string.Formatter and override some methods, or switch to a different formatting method, like string.Template.

you can escape the interpolation of {two} by doubling the curly brackets:
x = "this string takes two like {one} and {{two}}"
y = x.format(one=1)
z = y.format(two=2)
print(z) # this string takes two like 1 and 2
a different way to go are template strings:
from string import Template
t = Template('this string takes two like $one and $two')
y = t.safe_substitute(one=1)
print(y) # this string takes two like 1 and $two
z = Template(y).safe_substitute(two=2)
print(z) # this string takes two like 1 and 2
(this answer was before mine for the template strings....)

You can replace {two} by {two} to enable further replacement later:
y = x.format(one="one", two="{two}")
This easily extends in multiple replacement passages, but it requires that you give all keys, in each iteration.

All great answers, I will start using this Template package soon. Very disappointed in the default behavior here, not understanding why a string template requires passing all the keys each time, if there are 3 keys I can't see a logical reason you can't pass 1 or 2 (but I also don't know how compilers work)
Solved by using %s for the items I'm immediately substituting in the config file, and {key} for the keys I replace later upon execution of the flask server
In [1]: issue = "Python3 string {item} are somewhat defective: %s"
In [2]: preformatted_issue = issue % 'true'
In [3]: preformatted_issue
Out[3]: 'Python3 string {item} are somewhat defective: true'
In [4]: result = preformatted_issue.format(item='templates')
In [5]: result
Out[5]: 'Python3 string templates are somewhat defective: true'

Related

Managing keyword arguments in python strings: how to format only some arguments and keep the others unformatted

I have some troubles understanding the way the format() method of string works.
Suppose that I set a string variable with keywords arguments:
s = '{hello} {person_name}'
I could either assign this value to another variable or print it. In the latter case, the result would be {hello} {person_name}.
I could also use the format() method while printing s and assign some values to the keywords:
print(s.format(hello='hello', person_name='Alice'))
In this case, the result is hello Alice. Of course, I could also assign it to a new variable.
My problem arises when I want to use format only on one keyword:
print(s.format(hello='hello'))
or
a = s.format(hello='hello')
Both of them throw an error:
KeyError: 'person_name'
I want to be able to run something like :
s = '{hello} {person_name}'
a = s.format(hello='hello')
if something:
b = a.format(person_name='Alice')
else:
b = a.format(person_name='Bob')
print(b)
Is something like this possible or should I set all keywords when I use format()?
In your use case, you might consider escaping the {person} in the string:
# double brace the person_name to escape it for the first format
s = '{hello} {{person_name}}'
a = s.format(hello='hello')
# a = 'hello {person_name}'
if something:
b = a.format(person_name='Alice')
# b = 'hello Alice'
else:
b = a.format(person_name='Bob')
# b = 'hello Bob'
print(b)
With this method however you will need to follow the explicit order in which you escaped your variables. i.e. you must assign hello first and then person_name. If you need to be flexible about the order of things, I would suggest using a dict to construct the variables before passing it altogether:
# dict approach
s = '{hello} {person_name}'
# determine the first variable
d = {'hello':'hello'}
... do something
d.update({'person': 'Alice'})
# unpack the dictionary as kwargs into your format method
b = s.format(**d)
# b = 'hello Alice'
This gives you a bit more flexibility on the order of things. But you must only call .format() once all the variables are provided in your dict (at least it must have a default value), otherwise it'll still raise an error.
If you want to be more fancy and want the ability to print the field names at the absence of the variable, you can make your own wrapper function as well:
# wrapper approach
# We'll make use of regex to keep things simple and versatile
import re
def my_format(message, **kwargs):
# build a regex pattern to catch words+digits within the braces {}
pat = re.compile('{[\w\d]+}')
# build a dictionary based on the identified variables within the message provided
msg_args = {v.strip('{}'): v for v in pat.findall(message)}
# update the dictionary with provided keyword args
msg_args.update(kwargs)
# ... and of course, print it
print(message.format(**msg_args))
s = 'Why {hello} there {person}'
my_format(s, hello='hey')
# Why hey there {person}
my_format(s, person='Alice')
# Why {hello} there Alice
You can determine the default display (at the absence of a variable) you want by modifying the v in dictionary comprehension.
As per PEP 3101 If the index or keyword refers to an item that does not exist, then an IndexError/KeyError should be raised.
But you can create your own custom formatter class like this.
from string import Formatter
class MyStringFormatter(Formatter):
def get_value(self, key, args, kwds):
try:
return super().get_value(key, args, kwds)
except KeyError:
return "{%s}" % key
fmt = MyStringFormatter()
DEMO
s = "{hello} {person_name}"
keywords = {'hello': 'hello'}
a = fmt.format(s, **keywords)
print(a)
# This will print hello {person_name}
something = False
if something:
person_name = {'person_name': 'Alice'}
else:
person_name = {'person_name': 'Bob'}
b = fmt.format(a, **person_name)
print(b)
# This will print `hello Bob` if `something` is False, 'hello Alice' otherwise.
Is something like this possible or should I set all keywords when I use format()?
PEP-3101 says:
If the index or keyword refers to an item that does not exist, then an IndexError/KeyError should be raised.
So yes, if you are going to use keywords you would have to specify them all.
I think you have to define all the keywords while using format().
I would suggest a different approach using *args:
def printHello(*args):
print(' '.join([arg for arg in args]))
printHello('hello', 'Alice')
# hello Alice
printHello('hello')
# hello
You can send any number of words into this function.

Convert string to char in python

Is there a possibility (e.g. a built-in function) to convert a string to a char in python? For example, if I assign a value to the variable
p1=100
and then have the string
"p1",
is it possible to assign the value of p1 to a new variable t like
t=char("p1")
so that I get
print(t)->100 ?
Obviously, above mentioned solution does not work as it throws following error message:
NameError: name 'char' is not defined
Edit: The question is not about best practices: I know that I should do this with a dictionary, but what I am trying to do is to understand data types in Python and how to convert them. Convert a string to an integer is fairly easy, for example, so I was wondering if the same applies to string to char conversions.
The eval() command does solve the example's question, but it does not answer above mentioned problem. So I would edit the goal to
char("p11")=20
which should give me
print(p11) -> 20
No. Use a dictionary.
>>> names = {'p1': 100, 'p2': 200}
>>> t = names['p1']
>>> t
100
This will throw a KeyError if the name does not exist:
>>> t = names['p3']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'p3'
... but you can use dict.get and provide a default value:
>>> t = names.get('p3', 'default')
>>> t
'default'
where the default-default-value is None.
(By the way, this has nothing to do with converting strings to a char(acter?), your terminology is quite confusing.)
You're probably looking for eval(): https://docs.python.org/3.5/library/functions.html#eval
p1 = 100
print(eval('p1')) # prints 100
If you want to evaluate expressions you can also use exec()
p1 = 100
t = 'p1 += 99'
exec(t)
print(p1) # prints 199
But as other already pointed out, you should avoid it if possible.
timgeb provided a nice alternative with dictionaries for example.
If you want to turn a string into a global variable you could also do:
globals()['y'] = 5
print(y)
Note: when you run your code in a global scope (outside a function), you can also use locals()['y'] = 5 - but this will not work in a non-global scope!
if you want to change it into character then do not use "char"
in python the char is declared as chr
so you can go like:
chr(input('Enter here'))

Python how to shorten a uuuid and decode?

I'm trying to create a shortened ID for one of my models using the following method:
_char_map = string.ascii_letters+string.digits
def index_to_char(sequence):
return "".join([_char_map[x] for x in sequence])
def make_short_id(self):
_id = self.id
digits = []
while _id > 0:
rem = _id % 62
digits.append(rem)
_id /= 62
digits.reverse()
return index_to_char(digits)
#staticmethod
def decode_id(string):
i = 0
for c in string:
i = i * 64 + _char_map.index(c)
return i
Where self.id is a uuid i.e. 1c7a2bc6-ca2d-47ab-9808-1820241cf4d4, but I get the following error:
rem = _id % 62
TypeError: not all arguments converted during string formatting
This method only seems to work when the id is an int.
How can I modify the method to shorten a uuuid and decode?
UPDATE:
Thank you for the help. I was trying to find a way create an encode and decode method that took a string, made it shorter then decode it back again. The methods above can never work with a string (uuid) as pointed out,
The % operator is the string formatting or interpolation operator and does not return the remainder in Python when used with strings. It will try to return a formatted string instead.
I'm not sure what your input is, but try converting it using int so you can get the remainder of it.
Edit: I see your input now, not sure why I missed it. Here's one method of converting a UUID to a number:
import uuid
input = "1c7a2bc6-ca2d-47ab-9808-1820241cf4d4"
id = uuid.UUID(input)
id.int
# returns 37852731992078740357317306657835644116L
Not sure what you mean by "shorten", but it looks like you are trying to "base 62 encode" the UUID. If you use the function from this question you will end up with the following:
uuid62 = base62_encode(id.int)
# uuid62 contains 'RJChvUCPWDvJ7BdQKOw7i'
To get the original UUID back:
# Create a UUID again
id = uuid.UUID(int=base62_decode(uuid62))
id.hex
# returns '1c7a2bc6ca2d47ab98081820241cf4d4'
str(id)
# returns '1c7a2bc6-ca2d-47ab-9808-1820241cf4d4'
_id is string
>>> 11 % 2
1
>>> "11" % 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting
I would suggest using base64.urlsafe_b64encode() from the standard library, rather than rolling your own base62_encode() function.
You first need to convert your hex string to a binary string:
binary_id = id.replace("-", "").decode("hex")
This binary string can the be encoded using the afore-mentioned function:
shortened_id = base64.urlsafe_b64encode(binary_id)

How to get the value of the last assigned variable in iPython?

I am a total iPython newbie, but I was wondering if there is a way to get the value of the last assigned variable:
In [1]: long_variable_name = 333
In [2]: <some command/shortcut that returns 333>
In R we have .Last.value:
> long_variable_name = 333
> .Last.value
[1] 333
There's a shortcut for the last returned object, _.
In [1]: 1 + 3
Out[1]: 4
In [2]: _
Out[2]: 4
You can use IPython's In and Out variables which contain the commands/statements entered and the the corresponding output (if any) of those statements.
So, a naive approach would be to use those variables as the basis of defining a %last magic method.
However, since not all statements necessarily generate output, In and Out are not synchronous.
So, the approach I came up with was to parse In, and look for the occurrences of = and parse those lines for the output:
def last_assignment_value(self, parameter_s=''):
ops = set('()')
has_assign = [i for i,inpt in enumerate(In) if '=' in inpt] #find all line indices that have `=`
has_assign.sort(reverse=True) #reverse sort, because the most recent assign will be at the end
for idx in has_assign:
inpt_line_tokens = [token for token in In[idx].split(' ') if token.strip() != ''] #
indices = [inpt_line_tokens.index(token) for token in inpt_line_tokens if '=' in token and not any((c in ops) for c in token)]
#Since assignment is an operator that occurs in the middle of two terms
#a valid assignment occurs at index 1 (the 2nd term)
if 1 in indices:
return ' '.join(inpt_line_tokens[2:]) #this simply returns on the first match with the above criteria
And, lastly to make that your own custom command in IPython:
get_ipython().define_magic('last', last_assignment_value)
And, now you can call:
%last
And this will output the term assigned as a string (which may not be what you want).
However, there is a caveat to this: in that if you had entered incorrect input that involved assignment; e.g.: (a = 2), this method will pick it up. And, if your assignment involved variables: e.g. a = name, this method will return name and the not the value of name.
Given that limitation, you can then use the parser module to try and evaluate the expression like this (which can be appended to last_assignment_value in the last if statement):
import parser
def eval_expression(src):
try:
st = parser.expr(src)
code = st.compile('obj.py')
return eval(code)
except SyntaxError:
print 'Warning: there is a Syntax Error with the RHS of the last assignment! "%s"' % src
return None
However, given the possible evils of eval, I've left that inclusion up to you.
But, to be perfectly honest, a truly wholesome method would involve a parsing of the statement to verify the validity of the found input, as well as the input before it and more.
REFERENCES:
https://gist.github.com/fperez/2396341

Defining dynamic functions to a string

I have a small python script which i use everyday......it basically reads a file and for each line i basically apply different string functions like strip(), replace() etc....im constanstly editing the file and commenting to change the functions. Depending on the file I'm dealing with, I use different functions. For example I got a file where for each line, i need to use line.replace(' ','') and line.strip()...
What's the best way to make all of these as part of my script? So I can just say assign numbers to each functions and just say apply function 1 and 4 for each line.
First of all, many string functions – including strip and replace – are deprecated. The following answer uses string methods instead. (Instead of string.strip(" Hello "), I use the equivalent of " Hello ".strip().)
Here's some code that will simplify the job for you. The following code assumes that whatever methods you call on your string, that method will return another string.
class O(object):
c = str.capitalize
r = str.replace
s = str.strip
def process_line(line, *ops):
i = iter(ops)
while True:
try:
op = i.next()
args = i.next()
except StopIteration:
break
line = op(line, *args)
return line
The O class exists so that your highly abbreviated method names don't pollute your namespace. When you want to add more string methods, you add them to O in the same format as those given.
The process_line function is where all the interesting things happen. First, here is a description of the argument format:
The first argument is the string to be processed.
The remaining arguments must be given in pairs.
The first argument of the pair is a string method. Use the shortened method names here.
The second argument of the pair is a list representing the arguments to that particular string method.
The process_line function returns the string that emerges after all these operations have performed.
Here is some example code showing how you would use the above code in your own scripts. I've separated the arguments of process_line across multiple lines to show the grouping of the arguments. Of course, if you're just hacking away and using this code in day-to-day scripts, you can compress all the arguments onto one line; this actually makes it a little easier to read.
f = open("parrot_sketch.txt")
for line in f:
p = process_line(
line,
O.r, ["He's resting...", "This is an ex-parrot!"],
O.c, [],
O.s, []
)
print p
Of course, if you very specifically wanted to use numerals, you could name your functions O.f1, O.f2, O.f3… but I'm assuming that wasn't the spirit of your question.
If you insist on numbers, you can't do much better than a dict (as gimel suggests) or list of functions (with indices zero and up). With names, though, you don't necessarily need an auxiliary data structure (such as gimel's suggested dict), since you can simply use getattr to retrieve the method to call from the object itself or its type. E.g.:
def all_lines(somefile, methods):
"""Apply a sequence of methods to all lines of some file and yield the results.
Args:
somefile: an open file or other iterable yielding lines
methods: a string that's a whitespace-separated sequence of method names.
(note that the methods must be callable without arguments beyond the
str to which they're being applied)
"""
tobecalled = [getattr(str, name) for name in methods.split()]
for line in somefile:
for tocall in tobecalled: line = tocall(line)
yield line
It is possible to map string operations to numbers:
>>> import string
>>> ops = {1:string.split, 2:string.replace}
>>> my = "a,b,c"
>>> ops[1](",", my)
[',']
>>> ops[1](my, ",")
['a', 'b', 'c']
>>> ops[2](my, ",", "-")
'a-b-c'
>>>
But maybe string descriptions of the operations will be more readable.
>>> ops2={"split":string.split, "replace":string.replace}
>>> ops2["split"](my, ",")
['a', 'b', 'c']
>>>
Note:
Instead of using the string module, you can use the str type for the same effect.
>>> ops={1:str.split, 2:str.replace}
To map names (or numbers) to different string operations, I'd do something like
OPERATIONS = dict(
strip = str.strip,
lower = str.lower,
removespaces = lambda s: s.replace(' ', ''),
maketitle = lamdba s: s.title().center(80, '-'),
# etc
)
def process(myfile, ops):
for line in myfile:
for op in ops:
line = OPERATIONS[op](line)
yield line
which you use like this
for line in process(afile, ['strip', 'removespaces']):
...

Categories