noob question: "TypeError" wrong number of args

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Tue May 2 21:27:12 EDT 2006


Edward Elliott a écrit :
> bruno at modulix wrote:
> 
(snip)
> 
>>You skipped the interesting part, so I repost it and ask again: how
>>could the following code work without the instance being an explicit
>>parameter of the function to be used as a method ?
>>
>>def someFunc(obj):
>>  try:
>>    print obj.name
>>  except AttributeError:
>>    print "obj %s has no name" % obj
>>
>>import types
>>m = MyObj('parrot')
>>m.someMeth = types.MethodType(someFunc, obj, obj.__class__)
>>m.someMeth()
> 
> 
> I posted the only part that needs modification. 

Nope.

> Here it is again with the
> entire segment:
> 
> class MyObj(object):
>   def __init__(name):
>     self.name = name  <== interpreter binds name 'self' to object instance.
>                           compiler adds 'self' to method sig as 1st param.
> 
> def someFunc(obj):
>   try:
>     print obj.name  <== 'obj' gets bound to first arg passed.  when bound 
>                         as a method, first arg will be object instance.
>                         when called as func, it will be first actual arg.
>   except AttributeError:
>     print "obj %s has no name" % obj
> 
> import types
> m = MyObj('parrot')
> m.someMeth = types.MethodType(someFunc, obj, obj.__class__)  <== binds obj 
>                                    to first parameter of someFunc as usual
> m.someMeth()
> 
>  
> 
>>You see, wrapping a function into a method is not done at compile-time,
>>but at runtime. And it can be done manually outside a class statement.
>>In the above example, someFunc() can be used as a plain function.
> 
> 
> All the parameter information has been preserved. 
> Method signatures are
> unchanged from their current form,
> so the interpreter has no trouble
> deducing arguments.  You just don't actually declare self yourself. 

In this exemple, it was named 'obj', to make clear that there was 
nothing special about 'self'. As you can see from the call, I didn't 
actually passed the fist param, since the method wrapper takes care of 
it... So if we were to implement your proposition (which seems very 
unlikely...), the above code *would not work* - we'd get a TypeError 
because of the missing argument.

> When
> binding a function to an object as above, the interpreter sees and does
> exactly the same thing as now.

I'm sorry, but you're just plain wrong. *Please* take time to read about 
the descriptor protocol and understand Python's object model.

> 
>>This
>>wouldn't work with some automagical injection of the instance in the
>>function's local namespace, because you would then have to write
>>"method"'s code diffently from function's code.
> 
> 
> Maybe this will make it clearer:
> 
> Programmer's view     Compiler                           Interpreter's view
> def func (a, b)       func (a, b) -> func (a, b)         func (a, b)
> def method (a)        method (a) -> method (self, a)     method (self, a)
> 
> IOW the compiler adds 'self' to the front of the parameter list when
> processing a method declaration.

1/ there is *no* 'method declaration' in Python
2/ wrapping functions into methods happens at runtime, *not* at compile 
time.

(snip)

>>>And the rest should work fine.  When the interpreter sees a method
>>>declaration,
>>
>>The interpreter never sees a 'method declaration', since there is no
>>such thing as a 'method declaration' in Python. The def statement
>>creates a *function* object:
> 
> 
> Fine, whatever, compiler sees method declaration,

There ain't *nothing* like a 'method declaration' in Python. Zilch, 
nada, none, rien... All there is is the def statement that creates a 
*function* (and the class statement that creates a class object).

> 
>>>Complete non-sequitor, what does this have to do with self?
>>
>>It has to do that the obj.name() syntax doesn't imply a *method* call -
>>it can as well be a plain function call.  
> 
> Ok I see your point, 

Not quite, I'm afraid.

> 
>>Also, and FWIW: 
>>
>>>>>def moduleFunc():
>>
>>...     print self.name
>>...
>>
>>>>>moduleFunc()
>>
>>Traceback (most recent call last):
>>NameError: global name 'self' is not defined
> 
> 
> Exactly, that was my point in the first place.

I'm afraid we don't understand each other here. This was supposed to 
come as an illustration that, if some black magic was to 'inject' the 
instance (here named 'self') in the local namespace of a 'method' (the 
way you see it), we would loose the possibility to turn a function into 
a method. Try to re-read both examples with s/obj/self/ in the first one 
and s/self/obj/ in this last one.

Edward, I know I told you so at least three times, but really, 
seriously, do *yourself* a favor : take time to read about descriptors 
and metaclasses - and if possible to experiment a bit - so you can get a 
better understanding of Python's object model. Then I'll be happy to 
continue this discussion (.

FWIW, I too found at first that having to explicitely declare the 
instance as first param of a 'function-to-be-used-as-a-method' was an 
awful wart. And by that time (Python 1.5.2), it actually *was* a wart 
IMVHO - just like the whole 'old-style-class' stuff should I say. But 
since 'type-unification' and new-style-classes, the wart has turned into 
a feature, even if this only become obvious once you get a good enough 
understanding of how the whole damn thing works.

Following it's overall design philosophy, Python exposes (and so let you 
take control of) almost any detail of the object model implementation. 
The purpose here is to make simple things simple *and* complex things 
possibles, and the mean is to have a restricted yet consistent set of 
mechanisms. It may not be a jewel of pure beauty, but from a practical 
POV, it ends up being more powerful than what you'll find in most 
main-stream OOPLs - where simple things happens to be not so simple and 
complex things sometime almost impossible - and yet much more usable 
than some more powerful but somewhat cryptic OOPLs (like Common Lisp - 
which is probably the most astonishing language ever) where almost 
anything is possible but even the simplest things tend to be complex.

Oh, also - should I mention it here ? - Ruby is another pretty nice and 
powerful OOPL, with a more 'pure' object model (at least at first sight 
- I have not enough experience with it to know if it holds its 
promises). While Python is My Favourite Language(tm), I'm not too much 
religious about this, and can well understand that someone's feature is 
someone else's wart - thanks the Lord, everyone is different and unique 
-, and you may feel better with Ruby.



More information about the Python-list mailing list