On Class namespaces, calling methods

V

vsoler

Still learning python, especially OOP.

While testing classes, I sometimes think of them as "ordinary
containers" of values and functions (methods). That is, values and
functions can be grouped together inside "namespaces" calles classes.

class Uno:
a=1
def m():
print "mouse"

Say that I have this "silly" class.

While I can then write

print Uno.a

I cannot write
Uno.m()

I get the following error message:

TypeError: m() takes no arguments (1 given)

Since I have not created any instances of Uno, there is no self
object, and I do not understand what object is supplied to the
function call.

Could anybody explain what argument is being supplied to the method?
Is ther any workaround to call the m function?

Thank you
 
A

Alex Hall

Still learning python, especially OOP.

While testing classes, I sometimes think of them as "ordinary
containers" of values and functions (methods). That is, values and
functions can be grouped together inside "namespaces" calles classes.

class Uno:
a=1
def m(): def m(self):
print "mouse"
print "mouse" #I believe
Say that I have this "silly" class.

While I can then write

print Uno.a

I cannot write
Uno.m()

I get the following error message:

TypeError: m() takes no arguments (1 given)

Since I have not created any instances of Uno, there is no self
object, and I do not understand what object is supplied to the
function call.
I believe that self is always passed, whether you have an instance of
the uno class or not. Since you did not tell m() to expect self to be
passed to it, and self is passed anyway, m() has this self and has no
idea what to do with it. Also, you must make an uno object (u=uno())
then call methods off that object (u.m()) since not doing so means the
uno class will not get initialized.
 
V

vsoler

Which version of Python are you using? Python 2.6 gives:

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

whereas Python 3.1 gives:


mouse

(assuming you change the print statement to work on Python 3.x)

Hello Duncan,

I am using ver 2.6, under PythonWin

The feedback that I get from python is exactly this one:
Traceback (most recent call last):
 
V

vsoler

Which version of Python are you using? Python 2.6 gives:

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

whereas Python 3.1 gives:


mouse

(assuming you change the print statement to work on Python 3.x)

Hello Duncan,

Your error message is correct if you use class Uno(object):
However, the error message is different under class Uno:

Regards
 
L

Laszlo Nagy

class Uno:
a=1
def m():
print "mouse"

Say that I have this "silly" class.

While I can then write

print Uno.a

I cannot write
Uno.m()

I get the following error message:

TypeError: m() takes no arguments (1 given)
As a workaround, use this pattern:
.... @classmethod
.... def m(cls):
.... print "mouse"
....
mouse

Since I have not created any instances of Uno, there is no self
object, and I do not understand what object is supplied to the
function call.
The method is not actually called, so nothing is supplied. The error is
raised when it turns out that the number of actual parameters and the
number of formal parameters are different.

This is how you call a method:

#1. you try to call Uno.m() - the 'm' method object is found and taken
#2. argument values are evaluated and taken (in your example, there are
no arguments)
#3. an extra argument is inserted in front of the argument list. This
parameter is the object you where calling the method on. In this case,
it is the 'Uno' class.
#4. actual arguments are assigned to formal parameters. If this fails
for some reason, an exception is raised. In your case - the number of
actual parameters is one, the number of formal parameters is zero. This
is why you get an exception.
#5. If actual and formal parameters are matched, then the implicit self
parameter is checked. If you call a method, then it must be an instance
of the class being called (or an instanc of its subclass). For
classmethods, it must be the same class (or a subclass of it).
#6. Function body executed, value returned

The implicit parameter (the instance, or for classmethods, the class) is
ALWAYS added. So if you change your code:

class Uno:
def m(self):
pass

Then you won't get an exception in #4. But you will in #5, because
instance methods must be called on an instance, not on a class. You can
create a classmethod (e.g. with the @classmethod decorator) as shown
above, and it will work.
 
V

vsoler

As a workaround, use this pattern:

 >>> class Uno(object):
...     @classmethod
...     def m(cls):
...             print "mouse"
...
 >>> Uno.m()
mouse


The method is not actually called, so nothing is supplied. The error is
raised when it turns out that the number of actual parameters and the
number of formal parameters are different.

This is how you call a method:

#1. you try to call Uno.m() - the 'm' method object is found and taken
#2. argument values are evaluated and taken (in your example, there are
no arguments)
#3. an extra argument is inserted in front of the argument list. This
parameter is the object you where calling the method on. In this case,
it is the 'Uno' class.
#4. actual arguments are assigned to formal parameters. If this fails
for some reason, an exception is raised. In your case - the number of
actual parameters is one, the number of formal parameters is zero. This
is why you get an exception.
#5. If actual and formal parameters are matched, then the implicit self
parameter is checked. If you call a method, then it must be an instance
of the class being called (or an instanc of its subclass). For
classmethods, it must be the same class (or a subclass of it).
#6. Function body executed, value returned

The implicit parameter (the instance, or for classmethods, the class) is
ALWAYS added. So if you change your code:

class Uno:
    def m(self):
        pass

Then you won't get an exception in #4. But you will in #5, because
instance methods must be called on an instance, not on a class. You can
create a classmethod (e.g. with the @classmethod decorator) as shown
above, and it will work.

Hi Laszlo,

It's perfectly clear.

Thank you very much
 
P

Patrick Maupin

class Uno:
    a=1
    def m():
        print "mouse"

....

I cannot write
      Uno.m()

By default (at least in Python 2.x), Python will pass any function
which is accessed through getattr on class or instance (usually called
a "method") an instance of the class as the first parameter.

You can avoid this by using the @staticmethod decorator in front:

class Uno:
a = 1
@staticmethod
def m():
print "mouse"

Then you can call Uno.m() directly.

This puts a "staticmethod" wrapper around m(), which explains to the
interpreter not to try to pass it the instance variable.

Also useful is @classmethod.

Interestingly, if you don't use staticmethod, the value in the class's
dictionary is an ordinary function, but if you do use staticmethod,
you no longer have a callable function in the class's dictionary:
.... def m():
.... print "foo"
....
fred.__dict__['m']() foo
class fred:
.... @staticmethod
.... def m():
.... print "foo"
....
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'staticmethod' object is not callable


HTH,
Pat



Regards,
Pat
 
S

Steven D'Aprano

Anyway, the moral is never, ever to use old-style classes in Python 2.x.
You will get weird and unexpected results.

That's a bit strong. They're only weird and unexpected if you're not
expecting them and don't understand them.

Why are we worrying about the exact error message? New style or old
style, they both raise TypeError, and testing for the exact error message
is a fragile, dangerous thing to do: error strings are not part of the
Python API, and are subject to change without notice, for any reason. For
all we know, Python runtimes compiled on a Tuesday could use different
error messages from runtimes compiled on Wednesdays.

I can only think of two circumstances where old-style classes are
*wrong*: if you use multiple inheritance with a diamond diagram ("...now
you have THREE problems" *wink*), if you intend using descriptors such as
properties, or if you need __slots__. That's three circumstances:
multiple inheritance with a diamond diagram, descriptors, __slots__, and
__getattribute__. Four circumstances.

Any other time, they're merely discouraged, and gone in Python 3.x.
 
A

Aahz

I can only think of two circumstances where old-style classes are
*wrong*: if you use multiple inheritance with a diamond diagram ("...now
you have THREE problems" *wink*), if you intend using descriptors such as
properties, or if you need __slots__. That's three circumstances:
multiple inheritance with a diamond diagram, descriptors, __slots__, and
__getattribute__. Four circumstances.

Any other time, they're merely discouraged, and gone in Python 3.x.

Discouraged by some people. I certainly encourage the use of old-style
classes unless there's a specific reason to use new-style classes.
 
A

Aahz

For what reason?

Because it's simpler and because it allows people to write code that
works with 1.5.2+ if they want. Admittedly, few people want to go back
that far, but lots of people are still using Python 2.2, and I prefer to
avoid new-style classes with 2.2 because of the changes in 2.3.
 

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

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,174
Latest member
BlissKetoACV
Top