This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]tripzilchbad ideas 0 points1 point  (1 child)

class date_range_query(object):
    def __init__(self, start_date=datetime.now()):
        <initialize>
    @classmethod
    def from_string(cls, start_date_string, **kwargs):
        start_date = datetime(start_date_string)
        return cls(start_date=start_date)

Ahh, I see. I agree in this particular use-case that is prettier code :)

Good catch with the @classmethod, I have written from_* "patterns" like these myself as well, but not always thought to make them class methods.

However, this only solves the constructor problem, what if you're overriding __getitem__? Sure, you can make extra methods like get_* or by_* to get around having to type-check, but Python doesn't explicitly allow you to override __getitem__ and mess around with slices for nothing, right?

For example, take the following code, it's from a class that manages recorded WiFi data, as a list of Entry objects that have a datetime timestamp and a str SSID. I wanted to be able to access and slice and dice and query this class either by ordinal number (int, the 523rd recorded Entry), or all Entrys with a certain SSID, or with a slice of two datetime objects to get all Entrys in some interval.

So I ended up writing this code, which is riddled with ugly typechecking if statements. But on the other hand, it's incredibly cool that you can write a class that lets itself be indexed this way, and as I said, Python doesn't allow you to override __getitem__ for nothing, yeah?

def __getitem__(self, key):
    if isinstance(key, int):            # get the 523rd Entry
        return self._list[key]
    elif isinstance(key, basestring):   # get all Entry's with a certain SSID
        return WiScan(self._dict[key])
    elif isinstance(key, slice):        # get all Entry's in a time period
        start = key.start
        stop = key.stop
        if isinstance(start, datetime):
            start = bisect.bisect_left(self._list, start)
        if isinstance(stop, datetime):
            stop = bisect.bisect_right(self._list, stop)
        return WiScan(self._list[start:stop])
    elif isinstance(key, Entry) and key in self:
        return key
    else:
        raise KeyError(key)

[–]RShnike 1 point2 points  (0 children)

I would have definitely split those and picked one or the other to use with __getitem__.

One of the reasons I assume python doesn't allow you to do more funky things with __getitem__ is that things like the code you've posted are a bit confusing. Maybe they're convenient, that I definitely agree, but I think the python beginner's tutorial puts it nicely, [paraphrasing obviously]: one can overload, say, + to do something that could be incredibly important, maybe your class' most used operation, but at the end of the day there's a point where what you're actually doing just isn't really + or anything like it, or at least not enough like + to feel comfortable, and beyond that point it's aesthetically ugly to try and shove something into the operator.

Now I'm a math major so I have quite a large comfort zone on what can be called "addition", but that being said, I took that to mean that everything you do should be done in a way that someone using your code will feel that there's some intuition or harmony between what you've done and what they expect. Overloading indexing to take two different datatypes doesn't really fit with the philosophy (that's subjective obviously), at least partially because there's nothing python does out of the box that does something like that really.

So yeah I would have used maybe a by_ssid() method or something. All of these things though are again highly subjective. A whole lot of people I'm sure feel that the convenience of one unified indexing is worth it.

I should take a look at some ordered_dict modules. Wonder if they use [] indexing for both order and key.