views:

233

answers:

7

I am writing a class in Python 2.6.2 that contains a lookup table. Most cases are simple enough that the table contains data. Some of the cases are more complex and I want to be able call a function. However, I'm running into some trouble referencing the function.

Here's some sample code:

class a:
    lut = [1,
           3,
           17,
           [12,34],
           5]

Where lut is static, and is expected to be constant as well.

and now I wan to do the following:

class a:
    def spam0(self):
        return (some_calculation_based_on_self)

    def spam1(self):
        return (a_different_calculation_based_on_self)

    lut = [1,
           3,
           17,
           [12,34],
           5,
           self.spam0
           self.spam1]

This doesn't compile because self.spam0 and self.spam1 are undefined. I tried using a.spam but that is also undefined. How can I set lut[5] to return a reference to self.spam?

Edit: This is what I plan to do:

(continuing the definition of class a): import inspect

# continue to somewhere in the definition of class a

def __init__(self, param):
    self.param = param

def eggs(self):
    tmp = lut[param]
    if (insect.isfunction(tmp)): # if tmp is self.spam()
        return tmp()             # should call it here
    return tmp

So I want to either return a simple value or run some extra code, depending on the parameter.

Edit: lut doesn't have to be a class property, but the methods spam0 and spam1 do need to access the class members, so they have to belong to the class.

I'm not sure that this is the best way to do this. I'm still in the process of working this out.

A: 

You're going to need to move the definition of a.lut outside of the definition of a.

class a():
    def spam():pass

a.lut = [1,2,3,a.spam]

If you think about it, this makes perfect sense. Using self wouldn't work because self is actually only defined for class methods for which you use the parameter "self". self has no special meaning in Python and is not a reserved word; it is simply the conventional argument passed to bound class methods, but could also be this, or foo, or whatever you want.

Referring to a.lut doesn't work because the definition of a isn't complete yet. How should Python know, at that point in the code, what a is? In the scope of a's class definition, a itself is still undefined.

Triptych
Note that in this solution (to later call the function via the lut on an instance x) you ALSO need `a.lut[-1](x)`, just like with the approach I suggest, because `a.spam` here is an unbound method and doesn't offer you any extra functionality wrt the function I'm using, just one extra error check.
Alex Martelli
@Alex, you don't have to call spam as a.lut[-1](x). I considered it more likely that the OP would iterate through a.lut and call whatever was callable. There may well be an unpythonic design error in this approach, or maybe it models his problem quite well. There's really not enough information to judge.
Triptych
+3  A: 

In the clas body, you're creating the class; there is no self, so you obviously cannot yet refer to self.anything. But also within that body there is as yet no a: name a gets bound AFTER the class body is done. So, although that's a tad less obvious, in the body of class a you cannot refer to a.anything either, yet.

What you CAN refer to are bare names of class attributes that have already been bound: for example, you could simply use 5, spam] at the end of your list lut. spam will be there as a function, as you say at one point that you want; not as a method, and most definitely NOT as a class method (I don't see classmethod ANYWHERE in your code, why do you think a class method would magically spring into existence unless you explicitly wrap a function in the classmethod builtin, directly or by decorator?) -- I suspect your use of "class method" does not actually refer to the class-method type (though it's certainly confusing;-).

So, if you later need to call that function on some instance x of a, you'll be calling e.g. a.lut[-1](x), with the argument explicitly there.

If you need to do something subtler it may be possible to get a bound or unbound method of some sort at various points during processing (after the class creation is done, or, if you want a bound instance method, only after a specific instance is instantiated). But you don't explain clearly and completely enough what exactly it is that you want to do, for us to offer very detailed help on this later-stage alternatives.

Alex Martelli
I actually don't know what a *`classmethod`* is in Python, so I can't have been referring to that. :-)
Nathan Fellman
+2  A: 

While you are in the scope of the class, you can just write

class A:
    def spam(self):
        pass

    lut = [1, 2, 3, spam]

a = A()
print a.lut

gives

[1, 2, 3, <function spam at 0xb7bb764c>]

Don't forget that this is a function in your lookup table, not a number as you probably intended. You probably want to solve another problem.

Otto Allmendinger
I indeed want a function, not a number. I'll call the function in with relevant parameters in this case.
Nathan Fellman
Yes, this is what I mean. How can I use `a.lut[3]` as a method call? a.lut[3]() doesn't work.
Nathan Fellman
You can make `lut` an instance of a class that defines `__getitem__`, but I don't think that is the problem you want to solve. I suggest you open another question and describe your problem in broader terms.
Otto Allmendinger
A: 

I'm not sure I understand the question. Is this what you mean?

class a:
    lut = [1,
           3,
           17,
           [12,34],
           5]

    def __init__(self):
        self.lut.append(self.spam)

    def spam(self, a):
        print "this is %s" % a

b = a()
b.lut[-1](4)

this will output: "this is 4".

giorgian
This is what I mean. Is there any way to do this *not* in the constructor, but rather as part of the definition of `lut[]`?
Nathan Fellman
I see that Otto Allmendinger shows how to do that here http://stackoverflow.com/questions/1351669/referencing-class-methods-in-class-lists-in-python/1351694#1351694
Nathan Fellman
No, Otto's answer gives you unbound methods instead of bound methods, so it won't work like this. You'd have to call it as `b.lut[-1](b, 4)`, which is awkward.
Glenn Maynard
This one has a serious bug: a.lut is shared. `b = a(); print b.lut; c = a(); print c.lut`
Glenn Maynard
A: 

Trivial:

class a:
    @classmethod
    def spam(cls):
        # not really pass, but you get the idea
        pass

    lut = [1,
           3,
           17,
           [12,34],
           5,
           spam]


assert a().lut[-1] == a.spam
assert a.spam() is None
ilya n.
and how do I call `a().lut[-1]` as a class method?
Nathan Fellman
So inside of `a` I can call `self.lut[-1](self)`. That does the trick for me.
Nathan Fellman
A: 

You want the function to be bound to the class. None of the answers seem to address this.

This won't be done automatically; when you do this:

class a:
    def spam(self): print self
    lut = [1, spam]

lut[1] is spam itself, not a bound method to an object, so you can't simply call lut[1](); you'd have to call lut[1](self).

If you specifically want to be able to include functions in the list that can be called directly, you need to arrange for the functions to be bound, which means referencing them from an instance and not the class. To do this, you'd probably want to initialize this list from __init__:

class a:
    def spam(self): print self
    def __init__(self):
        self.lut = [1, self.spam]

and now self.lut[1]() is correct, since it's a bound method.

This all has the advantage that other functions can be placed in the list for other purposes, possibly bound to other objects or otherwise not expecting a parameter.

It has the disadvantage that you aren't reusing the list between instances; this may or may not matter to you.

Glenn Maynard
+1  A: 

Remember that there's no such thing as static, or constant, in Python. Just make it easy to read. Here's an example which generates a cached version of lut per object:

class A(object):
    def __init__(self):
        self.__cached_lut = None

    def spam0(self):
        return (some_calculation_based_on_self)

    def spam1(self):
        return (a_different_calculation_based_on_self)

    @property
    def lut(self):
         if self.__cached_lut is None:
             self.__cached_lut = [1,
               3,
               17,
               [12,34],
               5,
               self.spam0()
               self.spam1()]
         return self.__cached_lut

a = A()
print a.lut
John Millikin
but in this case I'm calling both `spam0` and `spam1` each time I instantiate the class. These can be (and in fact are in my example) very time-consuming functions.By the way, what does `@property` mean?
Nathan Fellman