functions, classes, bound, unbound?

7

7stud

Here is some example code that produces an error:

class Test(object):
def greet():
print "Hello"

t = Test()
t.greet()
TypeError: greet() takes no arguments (1 given)

Ok. That makes sense. t.greet() is a "bound method", so something
automatically relays the instance object to greet(), and since greet()
is defined with no parameters-->Error.

Hmmm...I know from testing several examples that if I call a function
that's defined inside a class, and I use the class name preceding the
function call, then I have to send the instance manually. So, based
on that error message, I'll just call the method using the class name
and not send the instance object:

Test.greet()

TypeError: unbound method greet() must be called with Test instance as
first argument (got nothing instead)
 
F

Felipe Almeida Lessa

Here is some example code that produces an error:
[snip]

Why do people absolutely *love* to do weird and ugly things with
Python? Contests apart, I don't see lots of people trying this kind of
things on other (common) languages.

Say with me: "Python is powerful, but I'll use that power *only* for
beautiful and straightforward code."

Further reading:
http://www.python.org/doc/Humor.html#zen
 
S

Steven D'Aprano

Here is some example code that produces an error:

class Test(object):
def greet():
print "Hello"

t = Test()
t.greet()
TypeError: greet() takes no arguments (1 given)

Ok. That makes sense. t.greet() is a "bound method", so something
automatically relays the instance object to greet(), and since greet()
is defined with no parameters-->Error.

Hmmm...I know from testing several examples that if I call a function
that's defined inside a class, and I use the class name preceding the
function call, then I have to send the instance manually. So, based
on that error message, I'll just call the method using the class name
and not send the instance object:

Test.greet()

TypeError: unbound method greet() must be called with Test instance as
first argument (got nothing instead)


Is there a question hidden there somewhere, or are you just reporting on a
fact you have learned and are excited about?


Instance methods always need a self parameter, even if you call them from
the class. That's why you have to provide the instance by hand if you call
them from the class.

If you are trying to create a method that doesn't take a self argument,
have a look at the staticmethod function. There's also a classmethod
function as well.
 
S

Steven Bethard

7stud said:
Here is some example code that produces an error:

class Test(object):
def greet():
print "Hello"

t = Test()
t.greet()
TypeError: greet() takes no arguments (1 given) [snip]
Test.greet()

TypeError: unbound method greet() must be called with Test instance as
first argument (got nothing instead)

Methods in a class are generally expected to take an instance as their
first argument, but your greet() method takes no arguments. This is
because classes don't invoke the function directly, they convert it to
an 'unbound method' object::
... def greet():
... print 'Hello'
... Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
TypeError: unbound method greet() must be called with Test instance
as first argument (got nothing instead)

If you really want to get to the original function, there are a couple
of options. You can go through the class's __dict__, or you can wrap
your method with a @staticmethod decorator (which tells the class not to
wrap the function when you try to use it)::
>>> Test.__dict__['greet']
said:
>>> Test.__dict__['greet']() Hello
>>> class Test(object):
... @staticmethod
... def greet():
... print 'Hello'
... Hello

STeVe
 
7

7stud

...classes don't invoke the function directly, they convert it to
an 'unbound method' object::

... def greet():
... print 'Hello'
...
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
TypeError: unbound method greet() must be called with Test instance
as first argument (got nothing instead)

I think I found the rule in the GvF tutorial, which is essentially
what the error message says:
-------
When an unbound user-defined method object is called, the underlying
function (im_func) is called, with the restriction that the first
argument must be an instance of the proper class (im_class) or of a
derived class thereof.
--------

So, if you call a function using the syntax TheClass.theMethod(),
then you are required to use an instance object as an argument.

In section 3.2 The Standard Class Hierarchy, it also says this:
-------
When a user-defined method object is created by retrieving a user-
defined function object from a class, its im_self attribute is None
and the method object is said to be unbound. When one is created by
retrieving a user-defined function object from a class via one of its
instances, its im_self attribute is the instance, and the method
object is said to be bound.
--------

In that first sentence, is he talking about retrieving the user-
defined function object from the class using the class name, e.g:

MyClass.someFunc

Is there some other way to retrieve a user-defined function object
from a class other than using the class name or an instance?

If you really want to get to the original function, there are a couple
of options.

No. Just trying to figure out how some things work.
 
I

irstas

MyClass.someFunc

Is there some other way to retrieve a user-defined function object
from a class other than using the class name or an instance?

What Steven B. already said, MyClass.__dict__['someFunc'], is a
different way than MyClass.someFunc that produces different results.
Since the method is an attribute of a class, what other kinds of means
are you expecting to be possible?

You can use reflection to dig up MyClass-object from the module
dictionary if referring to object or class by name in the code is
something you want to get rid of.
 
7

7stud

MyClass.someFunc
Is there some other way to retrieve a user-defined function object
from a class other than using the class name or an instance?

What Steven B. already said, MyClass.__dict__['someFunc'], is a
different way than MyClass.someFunc that produces different results.

That doesn't seem to fit what GvR was talking about. From this
example:

class Test(object):
def greet():
print "Hello"

methObj = Test.__dict__["greet"]
print methObj.im_self

I get this output:

Traceback (most recent call last):
File "test1.py", line 7, in ?
print methObj.im_self
AttributeError: 'function' object has no attribute 'im_self'

So it doesn't look like a method object gets created when you retrieve
a function from a class like that. Compare to:

class Test(object):
def greet():
print "Hello"

methObj = Test.greet
print methObj.im_self

output:
None
Since the method is an attribute of a class, what other kinds of means
are you expecting to be possible?

I'm just wondering why in this description:
----
When a user-defined method object is created by retrieving a user-
defined function object from a class, its im_self attribute is None
and the method object is said to be unbound. When one is created by
retrieving a user-defined function object from a class via one of its
instances, its im_self attribute is the instance, and the method
object is said to be bound.
 
G

Gabriel Genellina

Is there some other way to retrieve a user-defined function object
from a class other than using the class name or an instance?

What Steven B. already said, MyClass.__dict__['someFunc'], is a
different way than MyClass.someFunc that produces different results.

methObj = Test.__dict__["greet"]
print methObj.im_self

Traceback (most recent call last):
File "test1.py", line 7, in ?
print methObj.im_self
AttributeError: 'function' object has no attribute 'im_self'

Because this way, you get a function, not a method. I'd say "read
something about descriptors" but...

Nicolas Bourbaki [a collective of french mathematicians writing under that
alias] said on a book about set theory: Any school boy can understand all
of this with a minimum of effort, but for truly understanding what we are
talking about, around four years of prior training on mathematics may be
required. (I can't find the exact citation).

I suggest first learn to use Python and then try to understand how it does
what it does, along the way, and looking for answers when you have a
question.
 
S

Steven Bethard

> Is there some other way to retrieve a user-defined function object
> from a class other than using the class name or an instance?

> What Steven B. already said, MyClass.__dict__['someFunc'], is a
> different way than MyClass.someFunc that produces different results.
That doesn't seem to fit what GvR was talking about. From this
example:

class Test(object):
def greet():
print "Hello"

methObj = Test.__dict__["greet"]
print methObj.im_self

I get this output:

Traceback (most recent call last):
File "test1.py", line 7, in ?
print methObj.im_self
AttributeError: 'function' object has no attribute 'im_self'


Yep. The thing in the class __dict__ is the original *function*. The
thing you get from something like ``Test.greet`` is the *method*.
Here's another way of looking at it::
... pass
... ... print 'Hello'
...
>>> greet
said:
>>> Test.greet = greet
>>> Test.__dict__['greet']
said:
>>> Test.__dict__['greet'] is greet
True

Note that ``Test.__dict__['greet']`` is the ``greet`` *function*, not
some wrapper of that function. When you access the class attribute
normally, Python creates an 'unbound method' object::
True

See that ``Test.greet`` gives you an 'unbound method' object which just
wraps the real 'function' object (which is stored as the 'im_func'
attribute)? That's because under the covers, classes are actually using
doing something like this::
>>> Test.__dict__['greet'].__get__(None, Test)
said:
>>> Test.greet == Test.__dict__['greet'].__get__(None, Test)
True

So if you want to get a method from a function, you can always do that
manually yourself::
<unbound method Test.greet>

Hope that helps,

STeVe
 
7

7stud

Here's another way of looking at it::

... pass
...
... print 'Hello'
...
<unbound method Test.greet>

Interesting. After playing around with that example a bit and finally
thinking I understood bound v. unbound, I found what appears to be an
anomaly:
------------
class Test(object):
pass

def greet(x):
print "hello"

Test.func = greet
print Test.func

t = Test()
print t.func

def sayBye(x):
print "bye"

t.bye = sayBye
print t.bye
------------output:
<unbound method Test.greet>
<bound method Test.greet of <__main__.Test object at 0x6dc50>>
<function sayBye at 0x624b0>

Why doesn't t.bye cause a method object to be created?

... under the covers, classes are actually using
doing something like this::
Test.__dict__['greet'].__get__(None, Test)
Test.greet == Test.__dict__['greet'].__get__(None, Test)
True

....via __getattribute_, right?
... if you want to get a method from a function, you can always do that
manually yourself::

<unbound method Test.greet>

Manually creating a method object. Nice.
 
I

irstas

Interesting. After playing around with that example a bit and finally
thinking I understood bound v. unbound, I found what appears to be an
anomaly:
------------
class Test(object):
pass

def greet(x):
print "hello"

Test.func = greet
print Test.func

t = Test()
print t.func

def sayBye(x):
print "bye"

t.bye = sayBye
print t.bye
------------output:
<unbound method Test.greet>
<bound method Test.greet of <__main__.Test object at 0x6dc50>>
<function sayBye at 0x624b0>

Why doesn't t.bye cause a method object to be created?

Because that would be bad and unexpected behaviour. I might want to
store callback functions to an object as attributes. It wouldn't make
sense for the functions to be bound to the container object when I'd
access them.

Of course, "Test.func = greet; print Test.func" is also "unexpected
behaviour" as Test.func is not same as Test.__dict__['func']. But it's
unexpected in a good way, because it facilitates the way classes are
usually used. The whole point of the magic is to make common idioms
simple.
 
B

Bruno Desthuilliers

7stud a écrit :
Interesting. After playing around with that example a bit and finally
thinking I understood bound v. unbound, I found what appears to be an
anomaly:
------------
class Test(object):
pass

def greet(x):
print "hello"

Test.func = greet
print Test.func

t = Test()
print t.func

def sayBye(x):
print "bye"

t.bye = sayBye
print t.bye
------------output:
<unbound method Test.greet>
<bound method Test.greet of <__main__.Test object at 0x6dc50>>
<function sayBye at 0x624b0>

Why doesn't t.bye cause a method object to be created?

(technical answer - I leave to some guru the care to explain the
motivations for this behaviour)

Because t.bye is an attribute of instance t, not of class Test. A
descriptor object has to be a class attribute for the descriptor
protocol to be invoked.


Note that you can override __getattribute__ to change this - but this is
somewhat hackish, and will probably slow down things quite a bit.

FWIW, if you want to dynamically add a method to an instance:
inst.method = func.__get__(inst, inst.__class__)
 
7

7stud

def greet(x):
print "hello"
Test.func = greet
print Test.func
t = Test()
print t.func
def sayBye(x):
print "bye"
t.bye = sayBye
print t.bye
------------output:
<unbound method Test.greet>
<bound method Test.greet of <__main__.Test object at 0x6dc50>>
<function sayBye at 0x624b0>


Because t.bye is an attribute of instance t, not of class Test. A
descriptor object has to be a class attribute for the descriptor
protocol to be invoked.

Hurray! Like this:


t.func -> __getattribute__() -> __get__() -> create method object and
return it
t.bye -> __getattribute__() -> return sayBye

FWIW, if you want to dynamically add a method to an instance:
inst.method = func.__get__(inst, inst.__class__)

Nice.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top