Conditionally modify multiple variables - python

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

Related

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.

Using a dict to call functions from a string

I would like the first two words of a user input string to be read as function arguments for where to save the string. I've settled on using a dict instead of many if statements, but I'm not sure how to structure the dict.
I believe this is a correct start:
input: "question physics What happens to atoms when they are hit by photons?"
result: program saves the input in location questions\physics
raw_entry = input("Enter text in the following format: type subtype text")
instructions = raw_entry.split()[:2]
The two words (each being a "get_id" in the example) will designate where to save the text. This example seems to be what I'm looking for, but I'm not sure how to change it for my case.
function_dict = {'get_id':
(
# function
requests.get,
# tuple of arguments
(url + "/users/" + user,),
# dict of keyword args
{'headers': self.headers}
)
}
Let me know if I'm going about this logically or if it doesn't make sense. Thanks!
You will need to define the functions separately from the dictionary
For example:
def get_id():
... the function's code ...
function_dict = { "get_id":get_id, ... }
you can then call the function with its keyword:
function_dict["get_id"]()
but you can also do this without a dictionary if the keyword is the same as the function name:
globals()["get_id"]()

If thing exists, do something with thing

What is the correct (pythonic) way to do something like this?
var = 'The quick brown fox'
def exists(query, string):
if query in string:
return query
else:
return None
if thing = exists('fox', var):
print(thing.upper())
This is my example, but what I'm really trying to do is check if a Selenium web element exists. I want to avoid setting the result to a variable because that defeats the purpose of "exists". Also, I don't want to perform the search twice returning true/false the first time and then again, if it's true, to do something with it.
This is one of those cases where there's more than one way to do it. A few things you could do:
Treat the result as a collection of zero to one elements (it makes more sense if you call it something like find_one in such cases):
def find_one(query, string):
if query in string:
return [query]
return []
Then you can use the function in a for loop:
for existing_element in find_one(query, string):
# Do something with existing element
break
else:
# Here if we don't have any elements (note the `break` above)
Pass a callback as the first argument:
def if_exists(cb, query, string):
if query in string:
cb(query)
def run_on_valid_query(q):
# Do something with q
if_exists(run_on_valid_query, query, string)
Bite the bullet and use an intermediate variable:
result = extract_from(query, string)
if result: # Do work here

Is there a better way to create dynamic functions on the fly, without using string formatting and exec?

I have written a little program that parses log files of anywhere between a few thousand lines to a few hundred thousand lines. For this, I have a function in my code which parses every line, looks for keywords, and returns the keywords with the associated values.
These log files contain of little sections. Each section has some values I'm interested in and want to store as a dictionary.
I have simplified the sample below, but the idea is the same.
My original function looked like this, it gets called between 100 and 10000 times per run, so you can understand why I want to optimize it:
def parse_txt(f):
d = {}
for line in f:
if not line:
pass
elif 'apples' in line:
d['apples'] = True
elif 'bananas' in line:
d['bananas'] = True
elif line.startswith('End of section'):
return d
f = open('fruit.txt','r')
d = parse_txt(f)
print d
The problem I run into, is that I have a lot of conditionals in my program, because it checks for a lot of different things and stores the values for it. And when checking every line for anywhere between 0 and 30 keywords, this gets slow fast. I don't want to do that, because, not every time I run the program I'm interested in everything. I'm only ever interested in 5-6 keywords, but I'm parsing every line for 30 or so keywords.
In order to optimize it, I wrote the following by using exec on a string:
def make_func(args):
func_str = """
def parse_txt(f):
d = {}
for line in f:
if not line:
pass
"""
if 'apples' in args:
func_str += """
elif 'apples' in line:
d['apples'] = True
"""
if 'bananas' in args:
func_str += """
elif 'bananas' in line:
d['bananas'] = True
"""
func_str += """
elif line.startswith('End of section'):
return d"""
print func_str
exec(func_str)
return parse_txt
args = ['apples','bananas']
fun = make_func(args)
f = open('fruit.txt','r')
d = fun(f)
print d
This solution works great, because it speeds up the program by an order of magnitude and it is relatively simple. Depending on the arguments I put in, it will give me the first function, but without checking for all the stuff I don't need.
For example, if I give it args=['bananas'], it will not check for 'apples', which is exactly what I want to do.
This makes it much more efficient.
However, I do not like it this solution very much, because it is not very readable, difficult to change something and very error prone whenever I modify something. Besides that, it feels a little bit dirty.
I am looking for alternative or better ways to do this. I have tried using a set of functions to call on every line, and while this worked, it did not offer me the speed increase that my current solution gives me, because it adds a few function calls for every line. My current solution doesn't have this problem, because it only has to be called once at the start of the program. I have read about the security issues with exec and eval, but I do not really care about that, because I'm the only one using it.
EDIT:
I should add that, for the sake of clarity, I have greatly simplified my function. From the answers I understand that I didn't make this clear enough.
I do not check for keywords in a consistent way. Sometimes I need to check for 2 or 3 keywords in a single line, sometimes just for 1. I also do not treat the result in the same way. For example, sometimes I extract a single value from the line I'm on, sometimes I need to parse the next 5 lines.
I would try defining a list of keywords you want to look for ("keywords") and doing this:
for word in keywords:
if word in line:
d[word] = True
Or, using a list comprehension:
dict([(word,True) for word in keywords if word in line])
Unless I'm mistaken this shouldn't be much slower than your version.
No need to use eval here, in my opinion. You're right in that an eval based solution should raise a red flag most of the time.
Edit: as you have to perform a different action depending on the keyword, I would just define function handlers and then use a dictionary like this:
def keyword_handler_word1(line):
(...)
(...)
def keyword_handler_wordN(line):
(...)
keyword_handlers = { 'word1': keyword_handler_word1, (...), 'wordN': keyword_handler_wordN }
Then, in the actual processing code:
for word in keywords:
# keyword_handlers[word] is a function
keyword_handlers[word](line)
Use regular expressions. Something like the next:
>>> lookup = {'a': 'apple', 'b': 'banane'} # keyword: characters to look for
>>> pattern = '|'.join('(?P<%s>%s)' % (key, val) for key, val in lookup.items())
>>> re.search(pattern, 'apple aaa').groupdict()
{'a': 'apple', 'b': None}
def create_parser(fruits):
def parse_txt(f):
d = {}
for line in f:
if not line:
pass
elif line.startswith('End of section'):
return d
else:
for testfruit in fruits:
if testfruit in line:
d[testfruit] = True
This is what you want - create a test function dynamically.
Depending on what you really want to do, it is, of course, possibe to remove one level of complexity and define
def parse_txt(f, fruits):
[...]
or
def parse_txt(fruits, f):
[...]
and work with functools.partial.
You can use set structure, like this:
fruit = set(['cocos', 'apple', 'lime'])
need = set (['cocos', 'pineapple'])
need. intersection(fruit)
return to you 'cocos'.

how to rid "" from a string to non string

I have this
a = "'Something': False"
I want it to be this
a = 'Something': False
How do I do it? I can strip things within the double quotation marks, but not the quotation marks itself. Have researched, cant find. I have mind block at moment. Sorry
Im trying to do this:
query_results = UserProfile.objects.filter(Location: location, Gender: gender).extra(select={'rank': a > 0}, order_by=['-rank'])
where Location = Someplace and Gender = Male or Female.
But what about when i did not want a specific gender or location. The only thing i could think of to do was to do
Location__isnull:False, Gender__isnull:False
But i cant have both
Location:location, Location__isnull:False.
Thus i have to have the location argument as a variable.
How could i then do this. The information referring to Location and gender is coming from a request.GET
I cant post my code as i keep deleting and changing spaghetti to try make something edible.
For what you're trying to do, the easiest way is to build up a dictionary of the arguments you want to supply, then pass them to the filter() call. Here's some non-working sample code that might get you heading in the right direction.
arguments = {}
if location is not None:
arguments['Location'] = location
else:
arguments['Location__isnull'] = False
if gender is not None:
arguments['Gender'] = gender
else:
arguments['Gender__isnull'] = False
query_results = UserProfile.objects.filter(**arguments)
It's not clear from your question whether or not you really need to search these for records which are not null. An empty .filter() would return all the records just as if you'd called .all(), and if you use the pattern I've suggested, you could omit all the else clauses and just feed filter(**arguments) an empty dictionary.
In other words, you only need to specify the query terms you really require, and do that by adding them to the arguments dictionary. Then you call filter with **arguments. The ** is a special Python syntax that says "take all the key/value pairs in this dictionary and turn them into keyword arguments for this function."
You can't. This is not valid python syntax: 'Something': False ... unless it's inside a dictionary: {'Something': False}. And no, you can not assign 'Something': False to a variable.
stufftofilter = {}
if shouldFilterLocation:
stufftofilter['Location'] = 'thelocation'
# etc
query_set = UserProfile.objects.extra(select={'rank':a>0}, order_by=['-rank'])
if stufftofilter:
query_set.filter(**stufftofilter)

Categories