Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Everyone bumps into this at some time on their journey through Python, however personally it's not something I've contended with in years and there is good reason for that:

> Suppose you have a function which takes a person and allows you to update the person's name, age, both, or neither

The problem is not the lack of some fundamental feature, it is one of obviousness in interface design. A trixy interface as given by the example leads exactly to the kind of problems the library hopes to eliminate. Instead how about:

    def update(person, attrs):
        pass

    def update_with_email(person, attrs):
        update(person, attrs)
        send_email(person)
Not only is the problem avoided, but a problem of namespace pollution has been fixed too. Overusing keyword arguments in a hyper-generic manner forces extension of the code to require definition of a new function in order to avoid potential breakage.

For example, how does one add a 'use_html=True' parameter to update_with_email()? Perhaps by adding a 'use_html' kwarg that hopefully doesn't conflict with Person's attribute namespace, or perhaps by adding '_use_html', hoping to skirt the problem by introduction of ugliness. For a 'clean' backwards-compatible solution, we're forced into something like:

    def _real_update_with_email(person, use_html, kwargs):
        update(person, **kwargs)
        send_email(person, use_html=use_html)

    def update_with_email(person, **kwargs):
        _real_update_with_email(person, False, kwargs)

    def update_with_html_email(person, **kwargs):
        _real_update_with_email(person, True, kwargs)
How can the caller dynamically form the attribute names if they need to?

    # TODO: something seems terribly wrong here, I can't quite put my finger on it.
    update(person, **{'previous_' + attr: value})
etc.

I realize abuse of \* \* is very much a religious issue, and at first sight, one of the superficial attractions to Python (at least for me, way back in time), however with experience it seems to regularly introduce more problems than it solves outside a few niche uses. The idea of adding an 'undefined' value has been discussed going back years (try grepping python-ideas and python-dev) and it's never made it in for good reason.

There is one place where an 'undefined' might seem useful at first, for example in the implementation of `dict.pop()` where a missing second argument signals the need to raise KeyError. The problem is that no published, public value including 'undefined' can be used as placeholder without introducing another ugly rule to the language: the ability to use `pop(..., default)` with any default value except 'undefined'! (net simplicity gain: zero)



As a general rule of thumb (standard disclaimers apply), I've found it better to have several functions than one function, if that one function is going to do different operations according to the parameters passed in. ("Operation" is a vague term, but I think setting something vs not setting something would count.) It's all too common for the operation to end up being fixed at the call point, for every call point, and therefore for the path through each call to be the same each time. The parameter/argument system is the wrong mechanism for that.

This line of thinking was inspired by C's `fopen' (no doubt now that I've said that it's going to turn out that I'm the only person ever to have ended up using a string literal for the mode parameter 100% of the time). But I suspect it would be the case for this function too.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: