0

Please, move this question to Code Review -area. It is better suited there because I know the code below is junk and I wanted critical feedback to complete rewrite.

How can I write the set-to-constants relations in Python? So if A in a range, then return its corresponding constant.

[0,10]    <-> a
]10,77]   <-> b
]77,\inf[ <-> c

Smelling code, bad.

    # Bad style

    provSum=0


    # TRIAL 1: messy if-clauses
    for sold in getSelling():
            if (sold >=0 & sold <7700):
                    rate =0.1 
            else if (sold>=7700 & sold <7700):   
            #won't even correct mistakes here because it shows how not to do things
                    rate =0.15
            else if (sold>=7700):
                    rate =0.20


    # TRIAL 2: messy, broke it because it is getting too hard to read
    provisions= {"0|2000":0.1, "2000|7700":0.15, "7700|99999999999999":0.20}


    if int(sold) >= int(border.split("|")[0]) & int(sold) < int(border.split("|")[1]):
            print sold, rate
            provSum = provSum + sold*rate
Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
hhh
  • 50,788
  • 62
  • 179
  • 282
  • 1
    I would use the keyword `and` instead of `&` – rubik Jan 25 '11 at 16:38
  • `&` is semantically wrong here. It may work by accident, but it's utterly the wrong operator. – S.Lott Jan 25 '11 at 17:08
  • S.Lott: thank you for the notice. What I think you are aiming that is that `&` is a bitwise and while `and` is a logical and. Sorry about the err. – hhh Jan 25 '11 at 17:14

2 Answers2

3

If the list was longer than a mere three entries, I would use bisect.bisect():

limits = [0, 2000, 7700]
rates = [0.1, 0.15, 0.2]
index = bisect.bisect(limits, sold) - 1
if index >= 0:
    rate = rates[index]
else:
    # sold is negative

But this seems a bit overengineered for just three values...

Edit: On second thought, the most readable variant probably is

if sold >= 7700:
    rate = 0.2
elif sold >= 2000:
    rate = 0.15
elif sold >= 0:
    rate = 0.1
else:
    # sold is negative
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
1
if (sold >=0 & sold <7700):

is equivalent to

if 0 <= sold < 7700:

I'm not aware of a really great way to map ranges but this makes it much nicer looking at least.

You could use your 2nd approach too:

provisions = {(0, 2000) : 0.1, (2000,7700):0.15, (7700, float("inf")):0.20}

# loop though the items and find the first that's in range
for (lower, upper), rate in provisions.iteritems():
    if lower <= sold < upper:
        break # `rate` remains set after the loop ..

# which pretty similar (see comments) to
rate = next(rate for (lower, upper), rate in 
                 provisions.iteritems() if lower <= sold < upper)    
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
  • The `for` loop and the `next()` call are not equivalent in the case that `sold` is not contained in any interval. The loop will silently return the last rate (whatever that might be), `next()` will raise `StopIteration`. The latter seems preferable. – Sven Marnach Jan 25 '11 at 16:39
  • @Sven Marnach: Yeah, but i guess with the `inf` at least all positive numbers are covered in this case. But that's definitely something to keep in mind. – Jochen Ritzel Jan 25 '11 at 16:45
  • Adding `else: raise StopIteration` to the loop would make both apporaches *really* equivalent (except for leaking `lower` and `upper` into the current scope). – Sven Marnach Jan 25 '11 at 16:48
  • @Sven Marnach: I'd rather `raise SoldNotInAnyRange` instead of `StopIteration` in both cases, but I think any error checking is out of the scope of this question. – Jochen Ritzel Jan 25 '11 at 16:55