Andbdrew
Andbdrew

Reputation: 11895

How can I refer to a function not by name in its definition in python?

I am maintaining a little library of useful functions for interacting with my company's APIs and I have come across (what I think is) a neat question that I can't find the answer to.

I frequently have to request large amounts of data from an API, so I do something like:

class Client(object):
    def __init__(self):
        self.data = []

    def get_data(self, offset = 0):
        done = False
        while not done:
            data = get_more_starting_at(offset)
            self.data.extend(data)
            offset += 1
            if not data:
                done = True

This works fine and allows me to restart the retrieval where I left off if something goes horribly wrong. However, since python functions are just regular objects, we can do stuff like:

def yo():
    yo.hi = "yo!"
    return None

and then we can interrogate yo about its properties later, like:

yo.hi => "yo!"

my question is: Can I rewrite my class-based example to pin the data to the function itself, without referring to the function by name. I know I can do this by:

def get_data(offset=0):
    done = False
    get_data.data = []
    while not done:
        data = get_more_starting_from(offset)
        get_data.data.extend(data)
        offset += 1
        if not data:
            done = True
    return get_data.data

but I would like to do something like:

def get_data(offset=0):
    done = False
    self.data = [] # <===== this is the bit I can't figure out
    while not done:
        data = get_more_starting_from(offset)
        self.data.extend(data) # <====== also this!
        offset += 1
        if not data:
            done = True
    return self.data # <======== want to refer to the "current" object

Is it possible to refer to the "current" object by anything other than its name? Something like "this", "self", or "memememe!" is what I'm looking for.

Upvotes: 3

Views: 180

Answers (3)

agf
agf

Reputation: 176780

I don't understand why you want to do this, but it's what a fixed point combinator allows you to do:

import functools

def Y(f):
    @functools.wraps(f)
    def Yf(*args):
        return inner(*args)
    inner = f(Yf)
    return Yf

@Y
def get_data(f):
    def inner_get_data(*args):
        # This is your real get data function
        # define it as normal
        # but just refer to it as 'f' inside itself
        print 'setting get_data.foo to', args
        f.foo = args
    return inner_get_data

get_data(1, 2, 3)

print get_data.foo

So you call get_data as normal, and it "magically" knows that f means itself.

Upvotes: 3

Marcin
Marcin

Reputation: 49826

You could do this, but (a) the data is not per-function-invocation, but per function (b) it's much easier to achieve this sort of thing with a class.

If you had to do it, you might do something like this:

def ybother(a,b,c,yrselflambda = lambda: ybother):
    yrself = yrselflambda()
    #other stuff

The lambda is necessary, because you need to delay evaluation of the term ybother until something has been bound to it.

Alternatively, and increasingly pointlessly:

from functools import partial
def ybother(a,b,c,yrself=None):
    #whatever
    yrself.data = [] # this will blow up if the default argument is used
    #more stuff

bothered = partial(ybother, yrself=ybother)

Or:

def unbothered(a,b,c):
    def inbothered(yrself):
        #whatever
        yrself.data = []

    return inbothered, inbothered(inbothered)

This last version gives you a different function object each time, which you might like.

There are almost certainly introspective tricks to do this, but they are even less worthwhile.

Upvotes: 2

sberry
sberry

Reputation: 132018

Not sure what doing it like this gains you, but what about using a decorator.

import functools

def add_self(f):
    @functools.wraps(f)
    def wrapper(*args,**kwargs):
        if not getattr(f, 'content', None):
            f.content = []
        return f(f, *args, **kwargs)
    return wrapper

@add_self
def example(self, arg1):
    self.content.append(arg1)
    print self.content


example(1)
example(2)
example(3)

OUTPUT

[1]
[1, 2]
[1, 2, 3]

Upvotes: 1

Related Questions