Python and the self parameter
I'm having some issues with the self parameter, 开发者_开发百科and some seemingly inconsistent behavior in Python is annoying me, so I figure I better ask some people in the know. I have a class, Foo
. This class will have a bunch of methods, m1
, through mN
. For some of these, I will use a standard definition, like in the case of m1
below. But for others, it's more convinient to just assign the method name directly, like I've done with m2
and m3
.
import os
def myfun(x, y):
return x + y
class Foo():
def m1(self, y, z):
return y + z + 42
m2 = os.access
m3 = myfun
f = Foo()
print f.m1(1, 2)
print f.m2("/", os.R_OK)
print f.m3(3, 4)
Now, I know that os.access
does not take a self
parameter (seemingly). And it still has no issues with this type of assignment. However, I cannot do the same for my own modules (imagine myfun
defined off in mymodule.myfun
). Running the above code yields the following output:
3
True
Traceback (most recent call last):
File "foo.py", line 16, in <module>
print f.m3(3, 4)
TypeError: myfun() takes exactly 2 arguments (3 given)
The problem is that, due to the framework I work in, I cannot avoid having a class Foo
at least. But I'd like to avoid having my mymodule
stuff in a dummy class. In order to do this, I need to do something ala
def m3(self,a1, a2):
return mymodule.myfun(a1,a2)
Which is hugely redundant when you have like 20 of them. So, the question is, either how do I do this in a totally different and obviously much smarter way, or how can I make my own modules behave like the built-in ones, so it does not complain about receiving 1 argument too many.
os.access()
is a built-in function, in the sense that it's part of an extension module written in C. When the class is defined, Python doesn't recognize m2
as a method because it's the wrong type — methods are Python functions, not C functions. m3
, however, is a Python function, so it's recognized as a method and handled as such.
In other words, it's m2
that's exceptional here, not m3
.
One simple way to do what you want would be to make m3
a static method:
m3 = staticmethod(myfun)
Now the interpreter knows never to try and pass myfun()
a self
parameter when it's called as the m3
method of a Foo object.
I just want to add that the behaviour is not inconsistent as already Luke hinted.
Just try the following
print Foo.__dict__
{'__doc__': None,
'__module__': '__main__',
'm1': <function m1 at 0x02861630>,
'm2': <built-in function access>,
'm3': <function myfun at 0x028616F0>}
Here you can see that Python can't distinguish between m1 and m2. That's why both are evaluated to a bound-method.
A bound method is something like a method with an additional first argument pointing to an object: self.m(1, 2) -> m(self, 1, 2)
This binding behaviour is only implemented for User-defined methods. That explains why self.m2("/", os.R_OK)
is not evaluated to m2(self, "/", os.R_OK)
.
One last demo:
print Foo.m1
<unbound method Foo.m1>
print Foo.m2
<built-in function access>
print f.m1
<bound method Foo.m1 of <__main__.Foo instance at 0x02324418>>
print f.m2
<built-in function access>
Further information about the different function types can be found here:
http://docs.python.org/reference/datamodel.html
And as mentioned before this binding mechanism can also be prevented by using a staticmethod descriptor:
http://docs.python.org/library/functions.html#staticmethod
I think you're looking for staticmethod(). See docs here.
m2 = staticmethod(os.access)
m3 = staticmethod(myfun)
As to why m2 worked in your example and m3 didn't, that's not clear to me. Printing f.m2 and f.m3 in your original example reveals that f.m2 is a direct reference to built-in function os.access, while f.m3 is a bound method (bound to the instance f).
You should use the staticmethod function in this case. When writing static class methods, you can use it as a decorator:
class A:
def printValue(self,value):
print value
@staticmethod
def staticPrintValue(value):
print value
>>> A.printValue(5)
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
A.printValue(5)
TypeError: unbound method printValue() must be called with A instance as first argument (got int instance instead)
>>> A.staticPrintValue(5)
5
One way would be to manually apply the static method decorator:
class Foo(object):
m3 = staticmethod(mymodule.myfun)
精彩评论