In Python, I'd like to test for the existence of a keyword in the output of a Linux command. The keywords to test for would be passed as a list as shown below. I've not spent a lot of time with Python so brute-force approach is below. Is there a cleaner way to write this?
def test_result (result, mykeys):
hit = 0
for keyword in mykeys:
if keyword in result:
hit = 1
print "found a match for " + keyword
if hit == 1:
return True
result = "grep says awk"
mykeys = ['sed', 'foo', 'awk']
result = test_result (result, mykeys)
The any built-in will do it.
def test_result(result, mykeys):
return any(key in result for key in mykeys)
You can use a regular expression to accomplish this. A regular expression of the form a|b|c matches any of a, b or c. So, you'd want something of the form:
import re
p = re.compile('|'.join(mykeys))
return bool(p.search(result))
p.search(result) searches the entire string for a match of the regular expression; it returns a match (which is truth-y) if present and returns None (which is false-y) otherwise. Converting the result to bool gives True if it matches and False otherwise.
Putting this together, you'd have:
import re
def test_result(result, mykeys):
p = re.compile('|'.join(mykeys))
return bool(p.search(result))
You can also make this more concise by not pre-compiling the regular expression; this should be fine if it's a one-time use:
def test_result(result, mykeys):
return bool(re.search('|'.join(mykeys), result))
For reference, read about Python's re library.
Your function does two things, printing and returning the result. You could break them up like so:
def test_result(result, mykeys):
return [k in result for k in mykeys]
def print_results(results):
for result in results:
print("found a match for " + result)
test_result will return a list with all the found keys, or an empty list. The empty list is falsey, so you can use it for whatever tests you want. The print_results is only needed if you actually want to print, otherwise you can use the result in some other function.
If you only want to check for the presence and don't care about which key you found, you can do something like:
def test_result(result, my_keys):
return any(map(lambda k: k in result, mykeys))
If you're using python3 (as you should be), I believe this will be lazy and only evaluate as much of the list as necessary.
See A more concise way to write this Python code for a more concise version of this last function.
To search for an element in a list, you can use a for-else statement. In particular, this allows to return the found element.
def test_result (result, mykeys):
for keyword in mykeys:
if keyword in result: break
else:
return None
return keyword
print(test_result("grep says awk", ['sed', 'foo', 'awk'])) # 'awk'
print(test_result("grep says awk", ['bar', 'foo'])) # None
Related
I have a function I'm using to test in an if/then.
The issue is that I'm executing the function BOTH in the if conditional, and then again after the if statement because the function returns two items.
This just seems wasteful and I'm trying to think of ways to improve this. Here's a really basic version of what I'm trying to avoid: "True" is returned to allow the condition to pass, but then then "coolstuff()" is executed again to get more information from the function.
"coolstuff()" could possibly return false, so I can't use the returned string "stuff" as the test.
def coolstuff():
return True, "stuff"
if coolstuff()[0]:
coolthing = coolstuff()[1]
print coolthing
There's gotta be a better way to do this, no? My brain is melting a little as I try to hash it out.
I basically want to do something like this (invalid) syntax:
def coolstuff():
return True, "stuff"
if a, b == coolstuff() and a:
print b
Just collect both results into variables
a, b = fn()
if a:
# work with b
def coolstuff():
if valid:
return "stuff"
return None
data = coolstuff()
if data:
print(data)
Call the function and capture the entire returned value:
x = coolstuff()
Now you have access to both parts of the returned value, in x[0] and x[1].
Store it:
state, coolvar = coolstuff()
if state:
do_whatever(coolvar)
If in newer Python, you could use the dreaded walrus (but I prefer ti7's approach of just assigning in a separate line):
if (x := coolstuff())[0]:
print(x[1])
I'm testing the result of a function, which returns a list of long strings. Since I have line size limitations, I can't have the whole list on one line.
I already tried printing the list and that works, but I was wondering if there was another way without having to print the result of the function. What I got:
def my_function(input):
r"""Some description.
>>> import pprint
>>> pprint.pprint(my_function("input"))
['long_string_1',
'long_string_2',
'long_string_3']
"""
return list_of_long_strings
Is it possible to do this without printing, just calling the function and having the list with backlashes or something?
You can do it like this
def my_function(input):
r"""Some description.
>>> my_function("input") == ['long_string_1',
... 'long_string_2', 'long_string_3']
True
"""
list_of_long_strings = ['long_string_1', 'long_string_2', 'long_string_3']
return list_of_long_strings
I'm using a lambda function to extract the number in a string:
text = "some text with a number: 31"
get_number = lambda info,pattern: re.search('{}\s*(\d)'.format(pattern),info.lower()).group(1) if re.search('{}\s*(\d)'.format(pattern),info.lower()) else None
get_number(text,'number:')
How can I avoid to make this operation twice?:
re.search('{}\s*(\d)'.format(pattern),info.lower()
You can use findall() instead, it handles a no match gracefully. or is the only statement needed to satisfy the return conditions. The None is evaluated last, thus returned if an empty list is found (implicit truthiness of literals like lists).
>>> get_number = lambda info,pattern: re.findall('{}\s*(\d)'.format(pattern),info.lower()) or None
>>> print get_number(text, 'number:')
['3']
>>> print get_number(text, 'Hello World!')
>>>
That being said, I'd recommend defining a regular named function using def instead. You can extract more complex parts of this code to variables, leading to an easier to follow algorithm. Writing long anonymous function can lead to code smells. Something similar to below:
def get_number(source_text, pattern):
regex = '{}\s*(\d)'.format(pattern)
matches = re.findall(regex, source_text.lower())
return matches or None
This is super ugly, not going to lie, but it does work and avoids returning a match object if it's found, but does return None when it's not:
lambda info,pattern: max(re.findall('{}\s*(\d)'.format(pattern),info.lower()),[None],key=lambda x: x != [])[0]
I came up with this situation while writing code in python for my project and began to think on this problem.
The problem is given a string containing a function name with its arguments how do we get the arguments and the function name given the number of arguments in the function.
My first thought was:
s = 'func(a,b)'
index = s.find('(')
if(index != -1):
arg_list = s[index+1:-1].split(',')
func_name = s[:index]
But as I began to think more I realised what if function is specified within functions which has its own arguments?
func1(func2(a,b,c),func3(d,e))
With my above approach I will end up with right function name but arg_list will contain
["func2(a","b","c)","func3(","d","e)"]
How to generically solve this situation?
If your language looks sufficiently like Python, use ast.parse():
import ast
def parse(s):
tree = ast.parse(s)
print ast.dump(tree)
parse('f(a,b)')
All the information you need will be encoded in tree.
>>> import pyparsing as pyp
>>> def get_name_and_args(a_string):
index = a_string.find('(')
if index == -1:
raise Exception("No '(' found")
else:
root_function, a_string = a_string[:index], a_string[index:]
data = {}
data[root_function] = pyp.nestedExpr().parseString(a_string).asList()[0][0].split(',')
return data
>>> print get_name_and_args("func(a,b)")
{'func': ['a', 'b']}
This solves the simpler example you gave using the pyparsing module. I wasn't sure exactly how you wanted the output formatted, and it doesn't work for the nested example. However this should be enough to get you started
In Python, I'm trying to implement a pseudo-ternary operator within a template string. A value is inserted into a string if kwargs has a specific key.
re module has a way do exactly what I need in re.sub(), you can pass a function to be called on matches. What I can't do is to pass **kwargs to it. Code follows
import re
template_string = "some text (pseudo_test?val_if_true:val_if_false) some text"
def process_pseudo_ternary(match, **kwargs):
if match.groups()[0] in kwargs:
return match.groups()[1]
else:
return match.groups()[2]
def process_template(ts, **kwargs):
m = re.compile('\((.*)\?(.*):(.*)\)')
return m.sub(process_pseudo_ternary, ts)
print process_template(template_string, **{'pseudo_test':'yes-whatever', 'other_value':42})
line if match.groups()[0] in kwargs: is the problem of course, as process_pseudo_ternary's kwargs are empty.
Any ideas on how to pass these? m.sub(function, string) doesn't take arguments.
The final string is to be: some text val_if_true some text (because the dictionary has the key named 'pseudo_test').
Feel free to redirect me to a different implementation of ternary operator in a string. I'm aware of Python conditional string formatting . I need the ternary to be in the string, not in the string's formatting tuple/dict.
If i understand it correctly, you could use something like http://docs.python.org/library/functools.html#functools.partial
return m.sub(partial(process_pseudo_ternary, custom_1=True, custom_2=True), ts)
EDIT: Changed a little, to match your code better.