tags:

views:

375

answers:

6

Hello

My adventures in python continues and favorite books are silent again. Python offers a build-in waiy to test if a variable is inside an iterable object, using 'in' keyword:

if "a" in "abrakadabra" :
  print "it is definitely here"

But is it possible to test if more than one item is in list (any one)? Currently, i'm using syntax below, but it is kinda long:

if "// @in " in sTxt or "// @out " in sTxt or "// @ret " in sTxt or <10 more>
  print "found."

Of course regexp can help, but ewgexp will take big amount of code and will not be as clear as "a in b". Any other pythonic ways?

+5  A: 

any(snippet in text_body for snippet in ("hi", "foo", "bar", "spam"))

Benjamin Peterson
+24  A: 
alternatives = ("// @in ", "// @out ", "// @ret ")
if any(a in sTxT for a in alternatives):
    print "found"

if all(a in sTxT for a in alternatives):
   print "found all"

any() and all() takes an iterable and checks if any/all of them evaluate to a true value. Combine that with a generator expressions, and you can check multiple items.

MizardX
I added links to the documentation so S.Lott doesn't downvote you ;) In all seriousness, though, it's encouraged to link to the docs, hope you don't mind.
Paolo Bergantino
Not at all. Thanks.
MizardX
A: 

There's no built in way in the syntax to do it. However you can use the 'any' function to make it easier as @MizardX and @Benjamin Peterson showed.

Rory
+1  A: 

If you want any check then you would use this:

inthere = False
checks = ('a', 'b')

for check in checks:
    if check in 'abrakadabra':
        inthere = True
        break

If you want all check out you could use this:

inthere = True
checks = ('a', 'b')

for check in checks:
    if check not in 'abrakadabra':
        inthere = False
        break

EDIT: Didn't know the more pythonic any(). It's probably better to use that on python.

EDIT2: Added break statements, and corrected the all-case.

voyager
A: 

You could also use set methods and operators:

not alternatives.isdisjoint(sTxt)  # for "any"
(alternatives & sTxt) != set()  # Again, the intersection is nonempty
alternatives <= sTxt  # for "all"

I think these are easier to read than using the any or all, but have to convert your collections into sets. Since intersection and containment are what you care about, you might consider making them sets in the first place.

allyourcode
Doesn't work if sTxt is a string.
MizardX
My answer mentions that these objects have to be sets.
allyourcode
+4  A: 

If you're testing lots of lines for the same words, it may be faster to compile them as a regular expression. eg:

import  re
words = ["// @in ", "// @out ", "// @ret "] + ["// @test%s " % i for i in range(10)]

my_regex = re.compile("|".join(map(re.escape, words)))

for line in lines_to_search:
    if my_regex.search(line):  print "Found match"

Some quick timing shows that this is usually faster than the any(word in theString for word in words) approach. I've tested both approaches with varying text (short/long with/without matches). Here are the results:

         { No keywords  } |  {contain Keywords }
         short    long       short    long
regex  : 0.214    27.214     0.147    0.149
any in : 0.579    81.341     0.295    0.300

If performance doesn't matter though, the any() approach is more readable.

Brian
\o/ It's nice to have benchmarks with a "may be faster to.." claim!
dbr