views:

947

answers:

6

Say I am declaring a class C and a few of the declarations are very similar. I'd like to use a function f to reduce code repetition for these declarations. It's possible to just declare and use f as usual:

>>> class C(object):
...     def f(num):
...             return '<' + str(num) + '>'
...     v = f(9)
...     w = f(42)
... 
>>> C.v
'<9>'
>>> C.w
'<42>'
>>> C.f(4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f() must be called with C instance as first argument (got int instance instead)

Oops! I've inadvertently exposed f to the outside world, but it doesn't take a self argument (and can't for obvious reasons). One possibility would be to del the function after I use it:

>>> class C(object):
...     def f(num):
...             return '<' + str(num) + '>'
...     v = f(9)
...     del f
... 
>>> C.v
'<9>'
>>> C.f
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'C' has no attribute 'f'

But what if I want to use f again later, after the declaration? It won't do to delete the function. I could make it "private" (i.e., prefix its name with __) and give it the @staticmethod treatment, but invoking staticmethod objects through abnormal channels gets very funky:

>>> class C(object):
...     @staticmethod
...     def __f(num):
...             return '<' + str(num) + '>'
...     v = __f.__get__(1)(9)   # argument to __get__ is ignored...
... 
>>> C.v
'<9>'

I have to use the above craziness because staticmethod objects, which are descriptors, are not themselves callable. I need to recover the function wrapped by the staticmethod object before I can call it.

There has got to be a better way to do this. How can I cleanly declare a function in a class, use it during its declaration, and also use it later from within the class? Should I even be doing this?

+12  A: 

Quite simply, the solution is that f does not need to be a member of the class. I am assuming that your thought-process has gone through a Javaish language filter causing the mental block. It goes a little something like this:

def f(n):
    return '<' + str(num) + '>'

class C(object):

    v = f(9)
    w = f(42)

Then when you want to use f again, just use it

>>> f(4)
'<4>'

I think the moral of the tale is "In Python, you don't have to force everything into a class".

Ali A
I agree, it doesn't seem that f is related to C at all so why try to put it in the class
Andrew Cox
Thanks for the reality check; you're probably right that the simplest solution is just placing `f` outside. My instinct in putting it inside the class is a result of `f`'s intimate connection with the class and its declaration -- I don't want to bother the module namespace with it.
adrian
I would argue that this is what module namespaces are there for. If you want to signal that someone using the module shouldn't be bothered with f, prefix the name with an underscore (_f).
Jason Baker
+1 module namespace.
S.Lott
Agreed. def _f in module would be the normal simple Python way of doing it. You *could* jump through hoops to tie it into the class and hide it, but it doesn't really get you anything so unless there's a particular unusual requirement plump for the simple solution.
bobince
+2  A: 

This is one possibility:

class _C:
    # Do most of the function definitions in here
    @classmethod
    def f(cls):
        return 'boo'

class C(_C):
    # Do the subsequent decoration in here
    v = _C.f()
fivebells
+3  A: 

Extending Ali A's answer, if you really want to avoid f in the module namespace (and using a non-exported name like _f, or setting __all__ isn't sufficient), then you could achieve this by creating the class within a closure.

def create_C():
    def f(num):
        return '<' + str(num) + '>'

    class C(object):
        v = f(9)
        def method_using_f(self, x):  return f(x*2)
    return C

C=create_C()
del create_C

This way C has access to f within its definition and methods, but nothing else does (barring fairly involved introspection of its methods (C.method_using_f.im_func.func_closure))

This is probably overkill for most purposes though - documenting that f is internal by using the "_" prefix nameing convention should generally be sufficient.

[Edit] One other option is to hold a reference to the pre-wrapped function object in the methods you wish to use it in. For example, by setting it as a default argument:

class C(object):
    def f(num):
        return '<' + str(num) + '>'

    v = f(9)
    def method_using_f(self, x, f=f):  return f(x*2)

    del f

(Though I think the closure approach is probably better)

Brian
A: 

Let's begin from the beginning.

"declare a function in a class, use it during its declaration, and also use it later from within the class"

Sorry. Can't be done. "In a class" contradicts "used during declaration".

  • In a class means created as part of the declaration.
  • Used during declaration means it exists outside the class. Often as a meta class. However, there are other ways.

It's not clear what C.w and C.v are supposed to be. Are they just strings? If so, an external function f is the best solution. The "not clutter the namespace" is a bit specious. After all, you want to use it again.

It's in the same module as C. That's why Python has modules. It binds the function and class together.

import myCmod

myCmod.C.w
myCmod.C.v
myCmod.f(42)

If w and v aren't simple strings, there's a really good solution that gives a lot of flexibility.

Generally, for class-level ("static") variables like this, we can use other classes. It's not possible to completely achieve the desired API, but this is close.

>>> class F(object):
    def __init__( self, num ):
     self.value= num
     self.format= "<%d>" % ( num, )

>>> class C(object):
    w= F(42)
    v= F(9)

>>> C.w
<__main__.F object at 0x00C58C30>
>>> C.w.format
'<42>'
>>> C.v.format
'<9>'

The advantage of this is that F is a proper, first-class thing that can be extended. Not a "hidden" thing that we're trying to avoid exposing. It's a fact of life, so we might as well follow the Open/Closed principle and make it open to extension.

S.Lott
Its actually perfectly possible to use a function declared within the class while in the class declaration. It isn't yet attached to the class, but does exist within the class definition namespace. This is used for things like programatically generating methods (deleting the helper function after)
Brian
@Brian: Love to see an example. Perhaps you could update your example with this.
S.Lott
One example is creating setters and getters in a for loop within a class. Eg for name in ['foo','bar',...]: define a getter function, use them to create properties, and then delete the temporary getters at the end, leaving just the propertys.
Brian
(edited) Here's an example of doing this: http://scratchpad.cmlenz.net/4821326a0f68c52ba236bf7ea4b4af6b/
Brian
This is exactly the kind of use case I had in mind.
adrian
I'm confused. Brian's example is adrian's example #2. Yet, the original question said this was unacceptable. I guess I don't understand the question, then.
S.Lott
Oops. I guess not exactly -- I meant that this is what I had in mind for using functions in declarations in the first place. I just ran across another situation that benefitted from using the same function later on in my class. (Sorry for the confusion.)
adrian
They're exactly the same, but adrian says "not exactly". And there's already a selected answer that's NOT this solution.
S.Lott
+2  A: 

I believe you are trying to do this:

class C():
...     class F():
...         def __call__(self,num):
...             return "<"+str(num)+">"
...     f=F()
...     v=f(9)
>>> C.v
'<9>'
>>> C.f(25)
'<25>'
>>>

Maybe there is better or more pythonic solution...

"declare a function in a class, use it during its declaration, and also use it later from within the class"

Sorry. Can't be done.

"Can't be done" doesn't seem to get along with Python

"Can't be done" -- as stated. Your solution -- cleverly -- achieves the desired result, but does it a different way than stated. Different from your clever approach, the way stated in the OP can't be done.
S.Lott
+1  A: 

One option: write a better staticmethod:

class staticfunc(object):
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kw):
        return self.func(*args, **kw)
    def __repr__(self):
        return 'staticfunc(%r)' % self.func
ianb