Most elegant way to format multi-line strings in Python - python

I have a multiline string, where I want to change certain parts of it with my own variables. I don't really like piecing together the same text using + operator. Is there a better alternative to this?
For example (internal quotes are necessary):
line = """Hi my name is "{0}".
I am from "{1}".
You must be "{2}"."""
I want to be able to use this multiple times to form a larger string, which will look like this:
Hi my name is "Joan".
I am from "USA".
You must be "Victor".
Hi my name is "Victor".
I am from "Russia".
You must be "Joan".
Is there a way to do something like:
txt == ""
for ...:
txt += line.format(name, country, otherName)

info = [['ian','NYC','dan'],['dan','NYC','ian']]
>>> for each in info:
line.format(*each)
'Hi my name is "ian".\nI am from "NYC".\nYou must be "dan".'
'Hi my name is "dan".\nI am from "NYC".\nYou must be "ian".'
The star operator will unpack the list into the format method.

In addition to a list, you can also use a dictionary. This is useful if you have many variables to keep track of at once.
text = """\
Hi my name is "{person_name}"
I am from "{location}"
You must be "{person_met}"\
"""
person = {'person_name': 'Joan', 'location': 'USA', 'person_met': 'Victor'}
print text.format(**person)
Note, I typed the text differently because it lets me line up the text easier. You have to add a '\' at the beginning """ and before the end """ though.
Now if you have several dictionaries in a list you can easily do
people = [{'person_name': 'Joan', 'location': 'USA', 'person_met': 'Victor'},
{'person_name': 'Victor', 'location': 'Russia', 'person_met': 'Joan'}]
alltext = ""
for person in people:
alltext += text.format(**person)
or using list comprehensions
alltext = [text.format(**person) for person in people]

line = """Hi my name is "{0}".
I am from "{1}".
You must be "{2}"."""
tus = (("Joan","USA","Victor"),
("Victor","Russia","Joan"))
lf = line.format # <=== wit, direct access to the right method
print '\n\n'.join(lf(*tu) for tu in tus)
result
Hi my name is "Joan".
I am from "USA".
You must be "Victor".
Hi my name is "Victor".
I am from "Russia".
You must be "Joan".

Related

How to build a function that returns elements matching in txt. file and dictionary

I am new to Python, so apologies in advance if my question seems foolish.
I am trying to build a function that searches for keys and values of a nested dictionary (built from info in a csv file) inside a .txt file and returns all matching words. So far this is what I tried:
text = ['da#8970095-v4',
'd#30/04/2019',
'h#2.0',
'power of attorney']
clientlist = {'hong kong co.': {'Client Code': '897',
'Matter Code': '0095',
'Matter Name': 'Incorporation of Brazilian Subsidiary'},
'shanghai co.': {'Client Code': '965',
'Matter Code': '3569',
'Matter Name': 'Corporate Matters'}}
def term_tracker(document, term_variations):
terms = []
#If term_variations is a dictionary
if isinstance(term_variations, dict) == True:
for term in term_variations:
if any([str(term) in i for i in document]):
terms.append(term)
#If term_variations is a list
if isinstance(term_variations, list) == True:
for term in term_variations:
#If we find a term in the document, append that term to a list
if any([str(term) in i for i in document]):
terms.append(term)
return terms
For some reason my output is a blank list:
In: term_tracker(text, clientlist[clientname]) #text = .txt file
Out: []
I could build lists with information collected from my nested dictionary (e.g., only with keys, or only with values), but I am trying to keep my code as clean as possible and therefore want to avoid this.
The following is another part of my code that I am also having issues with. When I use my term_tracker function inside the client_summary variable and then try to write a .txt file with the information included in this variable, my .txt file comes out without the information that the function should return.
def string_cleaner(document):
document = document.replace('[', '')
document = document.replace(']', '')
document = document.replace("'", '')
document = document.replace('"', '')
return document
for documents in samples:
filename = 'Time Sheet-' + time.strftime("%Y%m%d-%H%M%S")
infile = open(path + 'Sample docs' + '/' + documents, 'r')
.
.
.
client_summary = ['Client: ' + str(term_tracker(text, clientlist[clientname]['Client Code']))]
client_summary = string_cleaner(str(client_summary))
outfile = open(path+'Automated work descriptions/'+filename,'w', encoding='utf-8')
outfile.write(client_summary)
outfile.close()
If I run client_summary my editor returns the output I want. However, this information is not being written in my .txt file. I assume this has to do with the problem I am having with my function because if I try the following alternative I get the information I want written in a .txt file:
client_codes_only = [val['Client Code'] for val in clientlist.values()]
>>> ['897', '965']
.
.
.
client_summary = ['Client: ' + str(term_tracker(text, client_codes_only))]
client_summary = string_cleaner(str(client_summary))
>>> 'Client: 965'
Can anyone help me to identify why is my code not giving the expected result (or suggest another efficient way to achieve my goal)?
Thanks in advance!
Your script is returning the key of the dictionary, and you want the values.
Substitute this:
if any([str(term_variations[term]) in i for i in document]):
Wherever you have "term" replace it with term_variations[term].
It's worth noting, that your logic matches '0095', in your example data, with 'da#8970095-v4' in your "text" list.
2nd part of question:
For starters, if Hong Kong Co is your client lookup, then this line of code: client_summary = ['Client: ' + str(term_tracker(text, clientlist[clientname]['Client Code']))]
is passing term_tracker(text,'897') into your function, which will return the empty list from term_tracker(). Which will then write nothing to your file.

Python Splitting A String to make several keys for a dictionary

so I am trying to write a function that will read a text file, extract the information it needs from a line of text, and then assign that information to a key in a python dictionary. However here is a problem i have.
def read_star_names(filename):
"""
Given the name of a file containing a star catalog in CSV format, produces a dictionary
where the keys are the names of the stars and the values are Henry Draper numbers as integers.
If a star has more than one name, each name will appear as a key
in the dictionary. If a star does not have a name it will not be
represented in this dictionary.
example return: {456: 'BETA', 123: 'ALPHA', 789: 'GAMMA;LITTLE STAR'}
"""
result_name = {}
starfile = open(filename, 'r')
for dataline in starfile:
items = dataline.strip().split(',')
draper = int(items[3])
name = str(items[6])
result_name[name] = draper
starfile.close()
return result_name
This is attempting to read this:
0.35,0.45,0,123,2.01,100,ALPHA
-0.15,0.25,0,456,3.2,101,BETA
0.25,-0.1,0,789,4.3,102,GAMMA;LITTLE STAR
The problem I am having is that what it returns is this:
{'ALPHA': 123, 'GAMMA;LITTLE STAR': 789, 'BETA': 456}
I want the GAMMA and the LITTLE STAR, to be seperate keys, but still refer to the same number, 789.
How should I proceed?
I tried splitting the line of text at the semicolon but then that added indexes and I had a hard time managing them.
Thanks.
You already have isolated the part that contains all the names, all you need to do is separate the names and make separate keys for each of them, as so
for i in name.split(";"):
result_name[i] = draper

Python Searching and removing an item plus the following two from a text file

I am just starting out with programming and am learning Python. I am having some troubles searching and removing from a text file. The text file contains a list of single spaced names. I need to have the user input a name and have it and the two following items removed from list.
Right now I am able to find and remove the searched for name and write the new list to the text file but I can't figure out how to remove the next two items. I tried using list.index to get the position of the searched for name but it gives the location of the first letter in the name. Is there a way that I can search the input word and get the location of the whole word ('bob','tom','jill') (0,1,2) and use this to do what I need done?
Thanks.
Assuming the contacts file is three lines per contact, an example file might look like this:
Fred
58993884
AnyTown
Mary
61963888
SomeCity
Bill
78493883
OtherTown
Anne
58273854
AnyCity
Script:
x = raw_input('Enter Name of Contact to Delete: ')
# do a case-insensitive match for names
target = x.strip().lower()
# create a list of all valid contacts
skip = False
contacts = []
with open('contacts.txt', 'r') as stream:
for index, line in enumerate(stream):
# check every third line
if not index % 3:
skip = (line.strip().lower() == target)
if skip:
print 'Removed Contact:', line
if not skip:
contacts.append(line)
# re-open the contacts file for writing
with open('contacts.txt', 'w') as stream:
stream.write(''.join(contacts))
Output:
$ python2 /home/baz/code/misc/test.py
Enter Name of Contact to Delete: Mary
Removed Contact: Mary
$ cat contacts.txt
Fred
58993884
AnyTown
Bill
78493883
OtherTown
Anne
58273854
AnyCity
Instead of manipulating the list string of the names it would be better to manipulate a list of string names. You can easily convert the "big string" into a list using string.split:
names_string = 'john robert jimmy'
names_list = names_string.split(' ') # names_list = ['john', 'robert', 'jimmy']
Now, you can easily add, remove or search names in this list, using basic list functions:
names_list.append('michael') # names_list = ['john', 'robert', 'jimmy', 'michael']
names_list.remove('robert') # names_list = ['john', 'jimmy', 'michael']
jimmy_position = names_list.index('jimmy') # jimmy_position = 1
Remember handling the exceptions when the element is not in the list.
To convert the list of names into a "big string" again, you can use string.join:
names_string = ' '.join(names_list)

python string manipulation

Going to re-word the question.
Basically I'm wondering what is the easiest way to manipulate a string formatted like this:
Safety/Report/Image/489
or
Safety/Report/Image/490
And sectioning off each word seperated by a slash(/), and storing each section(token) into a store so I can call it later. (Reading in about 1200 cells from a CSV file).
The answer for your question:
>>> mystring = "Safety/Report/Image/489"
>>> mystore = mystring.split('/')
>>> mystore
['Safety', 'Report', 'Image', '489']
>>> mystore[2]
'Image'
>>>
If you want to store data from more than one string, then you have several options depending on how do you want to organize it. For example:
liststring = ["Safety/Report/Image/489",
"Safety/Report/Image/490",
"Safety/Report/Image/491"]
dictstore = {}
for line, string in enumerate(liststring):
dictstore[line] = string.split('/')
print dictstore[1][3]
print dictstore[2][3]
prints:
490
491
In this case you can use in the same way a dictionary or a list (a list of lists) for storage. In case each string has a especial identifier (one better than the line number), then the dictionary is the option to choose.
I don't quite understand your code and don't have too much time to study it, but I thought that the following might be helpful, at least if order isn't important ...
in_strings = ['Safety/Report/Image/489',
'Safety/Report/Image/490',
'Other/Misc/Text/500'
]
out_dict = {}
for in_str in in_strings:
level1, level2, level3, level4 = in_str.split('/')
out_dict.setdefault(level1, {}).setdefault(
level2, {}).setdefault(
level3, []).append(level4)
print out_dict
{'Other': {'Misc': {'Text': ['500']}}, 'Safety': {'Report': {'Image': ['489', '490']}}}
If your csv is line seperated:
#do something to load the csv
split_lines = [x.strip() for x in csv_data.split('\n')]
for line_data in split_lines:
split_parts = [x.strip() for x in line_data.split('/')]
# do something with individual part data
# such as some_variable = split_parts[1] etc
# if using indexes, I'd be sure to catch for index errors in case you
# try to go to index 3 of something with only 2 parts
check out the python csv module for some importing help (I'm not too familiar).

Finding a small list of strings in a large list of strings (Python)

Hi I'm new to Python, so this may come across as a simple problem but I've been searching through Google many times and I can't seem to find a way to overcome it.
Basically I have a list of strings, taken from a CSV file. And I have another list of strings in a text file. My job is to see if the words from my text file are in the CSV file.
Let's say this is what the CSV file looks like (it's made up):
name,author,genre,year
Private Series,Kate Brian,Romance,2003
Mockingbird,George Orwell,Romance,1956
Goosebumps,Mary Door,Horror,1990
Geisha,Mary Door,Romance,2003
And let's say the text file looks like this:
Romance
2003
What I'm trying to do is, create a function which returns the names of a book which have the words "Romance" and "2003" in them. So in this case, it should return "Private Series" and "Geisha" but not "Mockingbird". But my problem is, it doesn't seem to return them. However when I change my input to "Romance" it returns all three books with Romance in them. I assume it's because "Romance 2003" aren't together because if I change my input to "Mary Door" both "Goosebumps" and "Geisha" show up. So how can I overcome this?
Also, how do I make my function case insensitive?
Any help would be much appreciated :)
import csv
def read_input(filename):
f = open(filename)
return csv.DictReader(f, delimiter = ',')
def search_filter(src, term):
term = term.lower()
for s in src:
if term in map(str.lower, s.values()):
yield s
def query(src, terms):
terms = terms.split()
for t in terms:
src = search_filter(src, t)
return src
def print_query(q):
for row in q:
print row
I tried to split the logic into small, re-usable functions.
First, we have read_input which takes a filename and returns the lines of a CSV file as an iterable of dicts.
The search_filter filters a stream of results with the given term. Both the search term and the row values are changed to lowercase for the comparison to achieve case-independent matching.
The query function takes a query string, splits it into search terms and then makes a chain of filters based on the terms and returns the final, filtered iterable.
>>> src = read_input("input.csv")
>>> q = query(src, "Romance 2003")
>>> print_query(q)
{'genre': 'Romance', 'year': '2003', 'name': 'Private Series', 'author': 'Kate Brian'}
{'genre': 'Romance', 'year': '2003', 'name': 'Geisha', 'author': 'Mary Door'}
Note that the above solution only returns full matches. If you want to e.g. return the above matcher with the search query "Roman 2003", then you can use this alternative version of search_filter:
def search_filter(src, term):
term = term.lower()
for s in src:
if any(term in v.lower() for v in s.values()):
yield s

Categories

Resources