tags:

views:

108

answers:

4

It seems that an acceptable answer to the question

What is a method?

is

A method is a function that's a member of a class.

I disagree with this.

class Foo(object):
    pass

def func():
    pass

Foo.func = func

f = Foo()

print "fine so far"
try:
    f.func()
except TypeError:
    print "whoops! func must not be a method after all"
  1. Is func a member of Foo?
  2. Is func a method of Foo?

I am well aware that this would work if func had a self argument. That's obvious. I'm interested in if it's a member of foo and in if it's a method as presented.

+5  A: 

You're just testing it wrong:

>>> class Foo(object): pass
... 
>>> def func(self): pass
... 
>>> Foo.func = func
>>> f = Foo()
>>> f.func()
>>> 

You error of forgetting to have in the def the self argument has absolutely nothing to do with f.func "not being a method", of course. The peculiar conceit of having the def outside the class instead of inside it (perfectly legal Python of course, but, as I say, peculiar) has nothing to do with the case either: if you forget to have a first argument (conventionally named self) in your def statements for methods, you'll get errors in calling them, of course (a TypeError is what you get, among other cases, whenever the actual arguments specified in the call can't match the formal arguments accepted by the def, of course).

Alex Martelli
But the _whole_ point of my example is that `func` _doesn't_ have a first (and hence, any) parameter and yet it is _still_ a member of `Foo`.
aaronasterling
@Alex, Can I take your answer as saying that `func`, as defined, _is_ a _method_ of `Foo`?
aaronasterling
`func` is a name. When used as a **bare** name, it's not bound to a method object. When used as a **qualified** name, i.e., an attribute of a name bound to the class or an instance thereof, the value it provides **is** a method object (as it happens, a different method object each and every time: `print f.func is f.func` shows `False`). Therefore, the assertion "func is a method" is neither true nor false: it's meaningless. (If the function object doesn't have a first parameter, the method object cannot be called correctly, but of course that does **not** mean it's not a method object!).
Alex Martelli
But then is func really a function at all if it requires a context parameter?
Falmarri
BTW, `print type(f.func)` does of course confirm this, i.e., it shows `InstanceMethod`, quite independently from what parameters `f.func` has or lacks. Why is it so hard for you to accept that this is the case?!
Alex Martelli
`but of course that does not mean it's not a method object!` Does that mean it **is** a method object then? Are we just arguing for the sake of arguing if we're trying to call a function a method object if that object isn't' callable?
Falmarri
@Falmarri, `type(x)` tells you what type `x` is. Whether `x` can then be correctly used in a certain way, or not, does **not** determine `type(x)` (although to some large extent, `type(x)` may constrain how `x` can be correctly used, the reverse doesn't hold). E.g., `def f(x): pass` means `f()` and `f(12,34)` will both produce `TypeError`: why would anybody **dream** to read this as "f not being a function"?!
Alex Martelli
Any callable produces `TypeError` unless called with a sequence (possibly empty) and/or named args within a certain set of acceptable ones; for a buggily written method, the set of acceptable sequences of args simply is the empty set. Empty sets are sets, just like empty sequences are sequences. `type(f.func)` says it's a method: **that's** what "means it **is** a method object", quite independently of what the acceptable set of args sequences may be. Seems obvious to me that the ones "arguing for the sake of arguing" are those who claim `type(f.func)` is lying!
Alex Martelli
`why would anybody dream to read this as "f not being a function"?` I don't think that's the argument. I think the argument is that it can be read as "f not being a method". Although I've totally lost myself in the semantics and have no idea what the original argument is, lol
Falmarri
@Alex. Thanks for clearing up my confusion. I was certain that I was right but I can see now that I was thinking about it incorrectly.
aaronasterling
@Aaron, you're most welcome!
Alex Martelli
A: 

f.func() will throw an error as written in the question. Since it won't find the attribute in 'Foo' namespace with name 'func'.

So my understanding is that method is an attribute in object namespace that is callable.

So if you did

f.func = func

Then attribute 'func' can be found in namespace of f , i.e. dir(f) and is callable and becomes a method of the object and not class Foo.

If we did Foo.func = func, it would become class method or callable.

The example code can be edited to see what is happening:

class Foo(object):
    pass

def func(t):
    print "XXXX"
    pass


class FoowithFunc(object):
    pass

    def func(self):
        pass

Foo.func = func

f = Foo()
t = FoowithFunc()
print dir(f)
print dir(t)
f.func()
print dir(f)
print dir(Foo)
pyfunc
You are wandering about incorrect things. The only problem in the originalk example is that the function she atributted to the class missed the implicit argument Python supplies when calling a method
jsbueno
+1  A: 

The type error wouldn't be thrown if func had a self argument, like any other instance method.

That's because when you evaluate f.func, you're actually binding f to the first argument of the function -- it then becomes a partial application which you can provide further arguments to.

If you want it to be a static method, then you need the staticmethod decorator which just throws away the first parameter and passes the rest into the original function.

So 2 ways of making it work:

def func(self): pass

-- or --

Foo.func = staticmethod(func)

depending on what you're aiming for.

Tom Whittock
A: 

As written, func is a member of Foo and a method of Foo instances such as your f. However, it can't be successfully called because it doesn't accept at least one argument.

func is in f's namespace and has a _call_() method. In other words, it is enough of a method that Python tries to call it like one when you invoke it like one. If it quacks like a duck, in other words...

That this call doesn't succeed is neither here nor there, in my opinion.

But perhaps a proper response to "But Doctor! When I don't accept any arguments in a function defined in a class, I get confused about whether it's really a method or not!" is simply "Don't do that." :-)

kindall