2

I want to write something like a AI in Python:

  1. My first code is like this:

    def run()
        if choice_a_is_avilable:
            choice_a()
            return
        elif choice_b_is_avilable:
            if choice_b_1_is_avilable:
                choice_b_1()
                return
            elif choice_b_2_is_avilable:
                choice_b_1()
                return
        else:
            choice_c()
    
    while 1:
        run()
    
  2. But the code to decide if choice_a_is_avilable is quite long, and condition should bind to method. I change the code to make it more clear.

    def run():
        if(choice_a()):
            return
        elif(choice_b()):
            return
        else(choice_c()):
            return
    
    def choice_b():
        if choice_b_is_avilable:
            if(choice_b_1()):
                return True
            elif(choice_b_2)
                return True
    
        return False
    
  3. When more and more choice comes into my code, it become more and more confusing and ugly, I considered using Exception:

    class GetChoiceException(Exception):
        """docstring for GetChoiceException"""
        def __init__(self, *args):
            super(GetChoiceException, self).__init__(*args)
    
    
    def run():
        try:
            choice_a()
            choice_b()
            choice_c()
        except GetChoiceException:
            pass
    
    def choice_a():
        if choice_a_is_avilable:
            some_function()
            raise GetChoiceException()
    

Is it a kind of abuse of Exception?

What is the write way to make choices in Python?

PaleNeutron
  • 2,543
  • 4
  • 25
  • 43
  • You can rewrite option 2 as `choice_a() or choice_b() or choice_c()` – khelwood Oct 03 '16 at 15:31
  • Check my answer from another question: http://stackoverflow.com/a/39816814/847552. I hope it can help you. – skovorodkin Oct 03 '16 at 15:33
  • I dont really like working through exceptions because it means you are coding something knowing it will break. If you program it right, it shouldnt ever throw an error.... so not the 3rd option. I would like to say that you should usually verify the critical variables prior to running your method, so i like 2 personally. That is because the method is more self contained than option 1. BUT... i flagged this because it is an opinion based question, and i gave you my *0.02* – Fallenreaper Oct 03 '16 at 15:33
  • 1
    I going the Exception route I'd suggest using a custom Exception class (doesn't have to do anything). e.g. `class ChoiceOK(Exception): pass`. That way the `exception` stack is protected from real errors. – hpaulj Oct 03 '16 at 15:44
  • @khelwood, but if I have at least 10 choices and some choices have branches, the code will become ... – PaleNeutron Oct 04 '16 at 00:45
  • @hpaulj, thx, that is what I really do, the code is just a sample and I have add the `class` to my question. – PaleNeutron Oct 04 '16 at 01:03

4 Answers4

0

If your choice_ functions are returning True if successful, False if unsuccessful, then you can try each in turn until one is successful with simply:

choice_a() or choice_b() or choice_c()

Because or is short-circuited, the expression will finish as soon as it finds an operand that returns true.

Or, if it seems more elegant, you could write that like this:

any(x() for x in (choice_a, choice_b, choice_c))

any is also short-circuited to stop as soon as it finds an operand that is true.

This would also allow you to maintain a list of function choices that are part of this operation, and use it like this:

choices = [choice_a, choice_b, choice_c]
...
any(x() for x in choices)
khelwood
  • 55,782
  • 14
  • 81
  • 108
0

There is nothing "wrong" in your exception method if this is what you want to do. To me it looks a bit static, though.

Not exactly knowing your problem, one alternative to consider is having a list of available function calls if it helps. You can store functions, classes and pretty much anything you want in lists. You can then pick one by random or by choice and execute that.

from random import choice

def foo():
    print "foo"

def bar():
    print "bar"

options = [foo,bar]
x = choice(options)
x()

would execute either foo() or bar(). You can then add or remove functions by modifying the contents of options list. If you just want to execute the first in list, you would call

options[0]()

Hope this helps.

Hannu

Hannu
  • 11,685
  • 4
  • 35
  • 51
0

Would this work:

choices = {'a': 1, 'b': 2}
result = choices.get(key, 'default')
SerialDev
  • 2,777
  • 20
  • 34
0

I'm a big fan of table-driven programs.

ctrl_table = [
   [choice_a_test, choice_a_func],
   [choice_b_test, choice_b_func],
   [choice_c_test, choice_c_func]]

for test, func in ctrl_table:
    if test():
        func()
        break
VPfB
  • 14,927
  • 6
  • 41
  • 75