开发者

Python and the self parameter

开发者 https://www.devze.com 2023-02-02 09:31 出处:网络
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

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)
0

精彩评论

暂无评论...
验证码 换一张
取 消