New user's initial thoughts / criticisms of Python

Joshua Landau joshua at landau.ws
Mon Nov 11 21:50:35 CET 2013


On 11 November 2013 10:39, Chris Angelico <rosuav at gmail.com> wrote:
> On Mon, Nov 11, 2013 at 9:09 PM,  <lorenzo.gatti at gmail.com> wrote:
>> Regarding the "select" statement, I think the most "Pythonic" approach is using dictionaries rather than nested ifs.
>> Supposing we want to decode abbreviated day names ("mon") to full names ("Monday"):
>
> You can't [normally], for instance, build up a
> dictionary that handles inequalities, but you can do that with elif.
> [...] Consider the following logic:
>
> A 'minor weapon' is based on a roll of a 100-sided dice. If it's 01 to
> 70, "+1 weapon: 2,000gp [weapon]"; if it's 71 to 85, "+2 weapon:
> 8,000gp [weapon]"; if 86 to 90, "Specific weapon [minor specific
> weapon]"; and if 91 to 100, "Special ability [minor special weapon]
> and roll again".
>
> My code to handle that starts out with this array:
>
> "minor weapon":({
>     70,"+1 weapon: 2,000gp [weapon]",
>     85,"+2 weapon: 8,000gp [weapon]",
>     90,"Specific weapon [minor specific weapon]",
>     100,"Special ability [minor special weapon] and roll again",
> }),
>
> (that's Pike; in Python it'd be a list, or maybe a tuple of tuples),
> and denormalizes it into a lookup table by creating 70 entries quoting
> the first string, 15 quoting the second, 5, and 10, respectively. So,
> with a bit of preprocessing, a lookup table (which in this case is an
> array (list), but could just as easily be a dict) can be used to
> handle inequalities.

The obvious way to me is a binary search:

    from bisect import bisect_left

    class FloorMap:
        def __init__(self, dct):
            self.indexes = sorted(list(dct))
            self.dct = dct

        def __getitem__(self, itm):
            index = self.indexes[bisect_left(self.indexes, itm)]
            return self.dct[index]


    minor_weapon = FloorMap({
        70:  "+1 weapon: 2,000gp [weapon]",
        85:  "+2 weapon: 8,000gp [weapon]",
        90:  "Specific weapon [minor specific weapon]",
        100: "Special ability [minor special weapon] and roll again"
    })

    minor_weapon[63]
    #>>> '+1 weapon: 2,000gp [weapon]'

The precise details of the wrapper class here are just to make
initialisation pretty; it could be done straight from a pair of lists
too:

    from bisect import bisect_left

    minor_weapon_indexes = 70, 85, 90, 100
    minor_weapon_descriptions = (
        "+1 weapon: 2,000gp [weapon]",
        "+2 weapon: 8,000gp [weapon]",
        "Specific weapon [minor specific weapon]",
        "Special ability [minor special weapon] and roll again"
    )

    minor_weapon_descriptions[bisect_left(minor_weapon_indexes, 80)]
    #>>> '+2 weapon: 8,000gp [weapon]'

Compare to

    if 80 <= 70:
        res = "+1 weapon: 2,000gp [weapon]"

    elif 80 <= 85:
        res = "+2 weapon: 8,000gp [weapon]"

    elif 80 <= 90:
        res = "Specific weapon [minor specific weapon]"

    elif 80 <= 100:
        res = "Special ability [minor special weapon] and roll again"

which although shorter¹ is a lot less data-driven and much less reusable.

¹ Longer if you ignore the import and class declaration.



More information about the Python-list mailing list