concatenation of string & integer NAMES within functions (python3) - python

This is the first posting I've done to this forum.
I have these strings, integers, and filenames declared early on...
linefeed='\n'
consonantFileHeader='Consonants found in the infile:'
vowelFileHeader='Vowels found in the infile:'
qConsonant=0
qVowel=0
ConsonantFile=open(path/tothe/file/chapters/Consonants.txt,'w+')
VowelFile=open(path/tothe/file/chapters/Vowels.txt,'w+')
the program reads in unicode glyphs one by one, and assigns each a 'type'.
if glyph='A':
type='VOWEL'
elif glyph='B':
type='CONSONANT'
... etc...
later on, we want to add to a running count of each 'type' and write a record to a file that shows all the occurrences of each 'type'. It's routine code, that we don't want cluttering up our main function, so we call another function to do it....
if type == 'CONSONANT':
tabulateCONSONANT(glyph)
elif type == 'VOWEL':
tabulateVOWEL(glyph)
at this point, two different types - two different functions. here they are...
## ------------------------------------------------------------
def tabulateCONSONANT(glyph):
qConsonant=qConsonant+1 # bump up a counter
if qConsonant = 1 # on 1 write header to output
ConsonantFile.write(consonantFileHeader)
ConsonantFile.write(glyph+linefeed) # write data after
return ;
## ------------------------------------------------------------
def tabulateVOWEL(glyph):
qVowel=qVowel+1 # bump up counter
if qVowel = 1
VowelFile.write(vowelFileHeader) # on 1, write header
VowelFile.write(glyph+linefeed) # write data after
return ;
fine and dandy, but this seems really redundant to me. even though only the actual NAMES of the values change, I have to call a different function for each type!
Is there any way write a single function that wherein we can concatenate the actual ITEM NAMES to do something like the following...?
if type == 'CONSONANT':
tabulateANYTHING(glyph,'Consonant')
elif type == 'VOWEL':
tabulateANYTHING(glyph,'Vowel')
## ------------------------------------------------------------
def tabulateANYTHING(glyph,TYPE):
# concatenate 'q'with 'TYPE' to reference 'qVowel'
qTYPE=qTYPE+1
if qTYPE = 1
# concatenate 'TYPE' with part of the filename > 'VowelFile'
TYPEFile.write(TYPEFileHeader)
TYPEFile.write(glyph+linefeed) # again,concatenation...
return ;
if you don't know what the heck I'm trying to do here, let me know and I'll try to make it clearer...

Create a dict or a collections.Counter and then inside tabulateANYTHING increment the value that has the key that matches TYPE
For example, if your dict is named quantities:
quantities[TYPE]+=1
For the files, create a dict of files and write to the one that has the key that matches TYPE.
Same thing for the file headers.

Related

Python: Using bool() to select which vars are TRUE, then use those values to call function

First question ever! I've built a GUI which asks user to input 2 of possible 5 values. Each pair of values (10 possible pairs) get used to run 10 different solution functions named Case_n to which all five values (both zero and non-zero) are passed.
The problem I'm having is getting the bool() results stripped down to 2 digit without brackets, etc and then placed into a variable used to create the name of the function to call.
I've run the logic, with TRUE values added to a list, then converted the list to a string so I could strip it down to just the numerals, saved the 2 digit string and added it to the Case_n name. Now, when I try to use the name to call the function, I get an error that a string is not callable. Please help . . .
s = 5 #vars. For this example, I've pre-loaded 2 of them
a = 15
l = 0
r = 0
e_deg = 0
ve = 0
case = []
if bool(s):
case.append(1)
if bool(a):
case.append(2)
if bool(l):
case.append(3)
if bool(r):
case.append(4)
if bool(e_deg):
case.append(5)
nm = str(case) # placeholder to convert case to string
case_num = nm[1] + nm[4] # this returns 12 as a string
# create case_num var, using the string
Case = "Case_" + case_num
print("Case = ",Case) # Should be Case_12
def Case_12(s,a,l,r,e_deg,ve):
print("Case_12 running")
Case(s,a,l,r,e_deg,ve) ```
You could just use eval(Case) but I advise against it as you are processing user input and it could be a security risk.
An easy way would be to build the following dict :
my_dict = {"Case_1": Case_1, ..., "Case_12" : Case_12}
And then, instead of calling Case, you would do
my_dict[Case](s,a,l,r,e_deg,ve)
You could also create a function :
def choose_case(my_case_as_str):
my_case_dict = {"Case_1": Case_1, ..., "Case_12": Case_12}
return my_case_dict[my_case_as_str]
And then call
choose_case(Case)(s,a,l,r,e_deg,ve)
By the way, you probably don't want your function and variable names to start with an uppercase letter. You also probably want to use a safer way to get user input (for example use Template str)

Applying a certain change to all elements in a list ( Python )

I'm working on a human stats database for a simulation game and cannot figure out a certain function. The data about each person is stored as a string in a humandict list. Every in-game year the ageup() func should be called, and change each strings data value.
This is the string data format that i use ( the list consists of these values which store data about every human ) :
##ID, age, strengths, smarts
I call the .split() method in order to divide those different numbers in a string to a list, and apply int() to each list item in order to use them in math. The ageup() function should access every humandict item and change the age value by 1. This is the code I currently use ( which doesn't work as intended ):
for unit in humandict:
human = unit.split()
age = int(human[1])
age += 1
replace = (str(human[0])+" "+str(age)+" "+str(human[2])+" "+str(human[3]))
humandict[int(human[0])] = replace
print(humandict)
The code successfully runs once, and the next time the function is called I then get the following error:
File "main.py", line 15, in ageup
human = unit.split()
AttributeError: 'NoneType' object has no attribute 'split'
I simply don't understand where the problem is arising, it can due to wrong ID assignment or something else. But I know for sure that using dictionary here is a better and efficient way to handle data.
So here is how you can implement the same stuff with dictionary:
human_list_of_dict = [{'ID':<int>, 'age':<int>, 'strengths':<str>, 'smarts':<str>}]
above written is a list of dictionary to store data right now it has only 1 dictionary in it but there can be as much as you need. then you simple call it just like a list with few changes.
for unit in human_list_of_dict:
unit['age'] = unit['age']+1
By this way you can save you hassle of converting string to list and vice-versa. Also code is efficient this way(since there is less data manipulation).

Trouble converting "for key in dict" to == for exact matching

Good morning,
I am having trouble pulling the correct value from my dictionary because there are similar keys. I believe I need to use the == instead of in however when I try to change if key in c_item_number_one: to if key == c_item_number_one: it just returns my if not_found: print("Specify Size One") however I know 12" is in the dictionary.
c_item_number_one = ('12", Pipe,, SA-106 GR. B,, SCH 40, WALL smls'.upper())
print(c_item_number_one)
My formula is as follows:
def item_one_size_one():
not_found = True
for key in size_one_dict:
if key in c_item_number_one:
item_number_one_size = size_one_dict[key]
print(item_number_one_size)
not_found = False
break
if not_found:
print("Specify Size One")
item_one_size_one()
The current result is:
12", PIPE,, SA-106 GR. B,, SCH 40, WALL SMLS
Specify Size One
To split the user input into fields, use re.split
>>> userin
'12", PIPE,, SA-106 GR. B,, SCH 40, WALL SMLS'
>>> import re
>>> fields = re.split('[ ,]*',userin)
>>> fields
['12"', 'PIPE', 'SA-106', 'GR.', 'B', 'SCH', '40', 'WALL', 'SMLS']
Then compare the key to the first field, or to all fields:
if key == fields[0]:
There are two usages of the word in here - the first is in the context of a for loop, and the second entirely distinct one is in the context of a comparison.
In the construction of a for loop, the in keyword connects the variable that will be used to hold the values extracted from the loop to the object containing values to be looped over.
e.g.
for x in list:
Meanwhile, the entirely distinct usage of the in keyword can be used to tell python to perform a collection test where the left-hand side item is tested to see whether it exists in the rhs-object's collection.
e.g.
if key in c_item_number_one:
So the meaning of the in keyword is somewhat contextual.
If your code is giving unexpected results then you should be able to replace the if-statement to use an == test, while keeping everything else the same.
e.g.
if key == c_item_number_one:
However, since the contents of c_item_number_one is a tuple, you might only want to test equality for the first item in that tuple - the number 12 for example. You should do this by indexing the element in the tuple for which you want to do the comparison:
if key == c_item_number_one[0]:
Here the [0] is telling python to extract only the first element from the tuple to perform the == test.
[edit] Sorry, your c_item_number_one isn't a tuple, it's a long string. What you need is a way of clearly identifying each item to be looked up, using a unique code or value that the user can enter that will uniquely identify each thing. Doing a string-match like this is always going to throw up problems.
There's potential then for a bit of added nuance, the 1st key in your example tuple is a string of '12'. If the key in your == test is a numeric value of 12 (i.e. an integer) then the test 12 == '12' will return false and you won't extract the value you're after. That your existing in test succeeds currently suggests though that this isn't a problem here, but might be something to be aware of later.

Conditionally modify multiple variables

Not quite sure what the correct title should be.
I have a function with 2 inputs def color_matching(color_old, color_new). This function should check the strings in both arguments and assign either a new string if there is a hit.
def color_matching(color_old, color_new):
if ('<color: none' in color_old):
color_old = "NoHighlightColor"
elif ('<color: none' in color_new):
color_new = "NoHighlightColor"
And so forth. The problem is that each of the arguments can be matched to 1 of 14 different categories ("NoHighlightColor" being one of them). I'm sure there is a better way to do this than repeating the if statement 28 times for each mapping but I'm drawing a blank.
You can at first parse your input arguments, if for example it's something like that:
old_color='<color: none attr:ham>'
you can parse it to get only the value of the relevant attribute you need:
_old_color=old_color.split(':')[1].split()[0]
That way _old_color='none'
Then you can use a dictionary where {'none':'NoHighlightColor'}, lets call it colors_dict
old_color=colors_dict.get(_old_color, old_color)
That way if _old_color exists as a key in the dictionary old_color will get the value of that key, otherwise, old_color will remain unchanged
So your final code should look similar to this:
def color_matching(color_old, color_new):
""" Assuming you've predefined colros_dict """
# Parsing to get both colors
_old_color=old_color.split(':')[1].split()[0]
_new_color=new_color.split(':')[1].split()[0]
# Checking if the first one is a hit
_result_color = colors_dict.get(_old_color, None)
# If it was a hit (not None) then assign it to the first argument
if _result_color:
color_old = _result_color
else:
color_new = colors_dict.get(_color_new, color_new)
You can replace conditionals with a data structure:
def match(color):
matches = {'<color: none': 'NoHighlightColor', ... }
for substring, ret in matches.iteritems():
if substring in color:
return ret
But you seems to have a problem that requires a proper parser for the format you are trying to recognize.
You might build one from simple string operations like "<color:none jaja:a>".split(':')
You could maybe hack one with a massive regex.
Or use a powerful parser generated by a library like this one

Removing duplicates from an attribute of a class variable

I'm extremely new to python and was having some trouble with removing duplicate values from an attribute of a class (I think this is the correct terminology).
Specifically I want to remove every value that is the same year. I should note that I'm printing only the first four value and searching for the first four values. The data within the attribute is actually in Yearmonthday format (example: 19070101 is the year 1907 on the first on january).
Anyways, here is my code:
import csv
import os
class Datatype:
'Data from the weather station'
def __init__ (self, inputline):
[ self.DATE,
self.PRCP] = inputline.split(',')
filename ='LAWe.txt'
LAWd = open(filename, 'r')
LAWefile = LAWd.read()
LAWd.close()
'Recognize the line endings for MS-DOS, UNIX, and Mac and apply the .split() method to the string wholeFile'
if '\r\n' in LAWefile:
filedat = LAWefile.split('\r\n') # the split method, applied to a string, produces a list
elif '\r' in LAWefile:
filedat = LAWefile.split('\r')
else:
filedat = LAWefile.split('\n')
collection = dict()
date= dict()
for thisline in filedat:
thispcp = Datatype(thisline) # here is where the Datatype object is created (running the __init__ function)
collection[thispcp.DATE] = thispcp # the dictionary will be keyed by the ID attribute
for thisID in collection.keys():
studyPRP = collection[thisID]
if studyPRP.DATE.isdigit():
list(studyPRP.DATE)
if len(date[studyPRP.DATE][0:4]):
pass #if year is seen once, then skip and go to next value in attribute
else:
print studyPRP.DATE[0:4] #print value in this case the year)
date[studyPRP.DATE]=studyPRP.DATE[0:4]
I get a this error:
Traceback (most recent call last):
File "project.py", line 61, in
if len(date[studyPRP.DATE][0:4]):
KeyError: '19770509'
A key error (which means a value isn't in a list? but it is for my data) can be fixed by using a set function (or so I've read), but I have 30,000 pieces of information I'm dealing with and it seems like you have to manually type in that info so that's not an option for me.
Any help at all would be appreciated
Sorry if this is confusing or nonsensical as I'm extremely new to python.
Replace this
if len(date[studyPRP.DATE][0:4])
by this
if len(date[studyPRP.DATE[0:4]]):
Explanation :
In the first line you are selecting the whole date as the key KeyError: '19770509' in the 4 first entry of date
In the correction you send the the first 4 character of the date(the year) in the dictionary
Don't know what exactly you want here. I'll reply based on I can help you on what.
Your error is because you are accessing your year in data before you are adding it.
Also, what you are adding to your collection is like
{
<object>.DATE: <object>
}
I don't know what you need here. Your lower for loop can be written as under:
for thisID in collection:
if thisID.isdigit():
if thisID[0:4] in date and len(date[thisID[0:4]]):
#if year is seen once, then skip and go to next
# value in attribute
pass
else:
print thisID[0:4] #print value in this case the year)
date[thisID[0:4]]=thisID[0:4]
Note your studyPRP.DATE is same as thisID.

Categories