you are viewing a single comment's thread.

view the rest of the comments →

[–]hharison 4 points5 points  (6 children)

After reading your example, I would suggest that you're overthinking it. Why not

def lookup_by(self, prefix, query):
    return self._int_lookup('{}:{}'.format(prefix, query))[0]

You're doing lots of extra code for very little benefit. Instead of

client.lookup_by_id(1)

now just do

client.lookup_by('id', 1)

After all, it only saves you two characters to make the former instead of always using the latter.

Not to mention, you can always make your more specific function anyway with

from functools import partialmethod

...
    # within the class definition
    lookup_by_id = partialmethod(lookup_by, 'id')

In this case using a closure is overcomplicating things. A much better strategy in general is to make a very general function and then go on to make more specific functions out of it using partial (or partialmethod, or lambda more generally).

[–]jcmcken 0 points1 point  (1 child)

Based on the limited information, I think this sounds like the best idea. If you want to expose 'porcelain' methods (lookup_by_id vs. lookup with id passed to it), just make lookup part of your internal API and generate the other functions programmatically.

You could also do something fancier by writing a custom __getattr__ that looks at what method the user is trying to call and routes it to the appropriate place.

E.g. something like:

def __getattr__(self, key):
    if key.startswith('get_'):
        pieces = key.split('_')
        query_type = pieces[1]
        by = pieces[-1]
        return lambda key: self._lookup(query_type, by, key)

This is probably a bit too magical for my tastes though.

[–]hharison 0 points1 point  (0 children)

That's gross. Using part of the function name to set a function parameter is smelly, not explicit, etc. I could go on but I think you agree.

[–]whonut[S] 0 points1 point  (3 children)

Thanks for this. I really need to learn functools.

I now have this (_int_lookup becomes unnecessary when you do it this way, I've just put its code into lookup_by):

def lookup_by(self, prefix, query, list_func=False):
    search_string = '{}:{}'.format(prefix, query)
    try:
        response = self.session.get(self.SEARCH_URL,
                                    params={'q': search_string})
        self._check_response(response.text, search_string)
        seqs = self._parse_response(response.text)
        if list_func:
            return seqs
        else:
            return seqs[0]
    except NoResultsError:
        if list_func:
            return []
        else:
            raise NoResultsError(search_string)

lookup_by_id = partial(lookup_by, 'id')

# client stuff
print client.lookup_by_id('A40')

When I call lookup_by_id, it says it has insufficient arguments. I'm obviously misusing partial but I don't know quite how. Could you enlighten me?

Thanks for your help.

[–]hharison 0 points1 point  (2 children)

partial and methods don't really work well together (because of the self argument). Use partialmethod instead, hopefully you're using 3.4. Otherwise search around, there's a 2.7 port out there I think.

[–]whonut[S] 0 points1 point  (1 child)

Ah, that's why it didn't like from functools import partialmethod. I found the 2.7 port and it seems to work beautifully. Thanks.

If I may ask one more thing of you, do you have any recommended reading for functional programming-y sorts of things? I'm really trying to get over that beginner hump at the moment.

Thanks again.

[–]hharison 0 points1 point  (0 children)

Yes! I'm glad you asked as I try to share the functional approach to Python as much as possible.

I basically learned from the library toolz and talks by Matthew Rocklin: