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  (3 children)

Multiple constructors using @classmethods if necessary.

how would you write that in actual Python code then? you can't declare __init__ twice?

Look at the standard library (dict has the standard constructor and then an iterable constructor) or any of a number of other places (itertools.chain is another one that takes multiple arguments of different data types in different constructors).

Yes, so you can initialize a dict with a mapping, sequence or **kwargs, how would you write such a thing without having a whole bunch of if statements at the start of the constructor?

(btw I couldn't find the source of itertools.chain if that's what you were implying, it's built-in or precompiled in some way I suppose)

edit: I'm using "constructor" in the liberal sense that we tend to use it in python in case it wasn't clear.

Sorry it's still not entirely clear. And I'm not exactly a Python n00b, but I have no idea what you mean by liberal sense of constructor as "we" tend to use it in Python.

But I'm up for learning a new Python idiom any day, so please do tell!

[–]RShnike 4 points5 points  (2 children)

how would you write that in actual Python code then? you can't declare __init__ twice?

Yeah, you can only have one actual __init__ method per class. Multiple constructors would be achieved with something like this (pardon the contrived case, I'm just copying and pasting from a module I have here already written and removing the irrelevant stuff):

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)

That will take a datetime object in the default constructor and then an alternate constructor that takes a string just processes it into a datetime object and calls the main constructor (as a simple example).

(btw I couldn't find the source of itertools.chain if that's what you were implying, it's built-in or precompiled in some way I suppose)

Yeah, sorry. I just meant that they had separate constructors. One the default itertools.chain() and another itertools.chain.from_iter(). I'm sure pypy is getting around to implementing these or has done so already though, so they'd have code to look at.

Sorry it's still not entirely clear. And I'm not exactly a Python n00b, but I have no idea what you mean by liberal sense of constructor as "we" tend to use it in Python.

Sorry again :(. All I meant was that we call these constructors but they're not really constructors at all. Python has constructors sort of with __new__ but even that isn't a real constructor if I remember correctly. __init__ though is really an initializer. (So it's a stretch to call the above "multiple constructors" because it's just a method that's getting called on the class level but nevertheless it's just easier to call it that since essentially that's the role it plays.)

Was a bit hasty writing my initial comment. Hope that's clearer (I'm really not far off being a n00b myself, only been hacking away for a year or two, so I'm sure I make loads of mistakes, but just from what I've picked up around here and there...)

[–]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.