1

I'm trying to learn using classes in Python, and have written this test program. It is to some extent based on code I found in another question I found here on Stack OverFlow.

The code looks as follows:

class Student(object):
    name = ""
    age = 0
    major = ""

    # The class "constructor" - It's actually an initializer 
    def __init__(self, name, age, major):
        self.name = name
        self.age = age
        self.major = major
    def list_values():
        print "Name: ", self.name
        print "Age: ", self.age
        print "Major: ", self.major

def make_student(name, age, major):
    student = Student(name, age, major)
    return student

print "A list of students."
Steve = make_student("Steven Schultz",23,"English")
Johnny = make_student("Jonathan Rosenberg",24,"Biology")
Penny = make_student("Penelope Meramveliotakis",21,"Physics")
Steve.list_values()
Johnny.list_values()
Penny.list_values()

When I run this, get the error "TypeError: list_values() takes no arguments (1 given)". In my oppinion I have not given any arguments, but I remove the parenthesis, giving the code

Steve.list_values
Johnny.list_values
Penny.list_values

This renders no errors, but does not do anything - nothing gets printed.

My questions:

  1. What's the deal with the parenthesis?
  2. What's the deal with the print statements?
user2536262
  • 279
  • 2
  • 8
  • 22

4 Answers4

1

Python requires you explicitly add the self arguments to member functions, you forgot to add self to your function decleration:

def list_values(self):

This defines it as a member function of the Student class. see here. When you call the functions as members of the Student instances, the self variable is added implicitly, thus triggering the exception, since you did not define a function named list_values that receives one parameter.

As for removing the parenthesis, this means you are not calling the functions but only referring to the function objects, doing nothing.

WeaselFox
  • 7,220
  • 8
  • 44
  • 75
  • What does that mean? Does it have any practical purpose? – user2536262 Apr 22 '14 at 08:28
  • Assuming you mean the `self` variable - ebarr gave a link to Guido(The guy who started python) covering this. check it out: http://neopythonic.blogspot.com.au/2008/10/why-explicit-self-has-to-stay.html – WeaselFox Apr 22 '14 at 08:31
  • That was interresting, but actually I meant the other thing, am I just mentioning the name of functions without executing them? It seems terribly strange. – user2536262 Apr 22 '14 at 08:41
  • Functions in python are first-class objects, meaning they can be reffered to by name and passed as parameters to functions/assigned to variables just like any other type. Using the ``( )`` operator is what actually ``calls`` the function (or other ``callable``) referenced by the name. – aruisdante Apr 22 '14 at 08:45
  • It is useful to pass a function object as an argument to another function for example. – WeaselFox Apr 22 '14 at 08:47
1

The list_values method needs to be bound to your Student instance:

You should change:

def list_values()

to:

def list_values(self)

For an explanation of why, see:

Also this blog post by Guido covers the subject too:

Community
  • 1
  • 1
ebarr
  • 7,704
  • 1
  • 29
  • 40
0

def list_values(): should be: def list_values(self):

In Python, instance methods take self as the first argument.

wong2
  • 34,358
  • 48
  • 134
  • 179
0

As others have mentioned, you should use self in list_values.

But you should really define one of the magic method __str__ or __repr__ instead. Additionally, you're setting some class properties that aren't necessary.

A simpler implementation would be:

class Student(object):

    def __init__(self, name, age, major):
        self.name = name
        self.age = age
        self.major = major

    def __str__(self):
        fs = "<name: {}, age: {}, major: {}>"
        return fs.format(self.name, self.age, self.major)

Use it like this:

In [9]: Steve = Student("Steven Schultz", 23, "English")

In [10]: Johnny = Student("Jonathan Rosenberg", 24, "Biology")

In [11]: Penny = Student("Penelope Meramveliotakis", 21, "Physics")

In [12]: print Steve
<name: Steven Schultz, age: 23, major: English>

In [13]: print Johnny
<name: Jonathan Rosenberg, age: 24, major: Biology>

In [14]: print Penny
<name: Penelope Meramveliotakis, age: 21, major: Physics>

Since you're not defining special methods, you might as well use a named tuple:

In [16]: from collections import namedtuple

In [17]: Student = namedtuple('Student', ['name', 'age', 'major'])

In [18]: Steve = Student("Steven Schultz", 23, "English")

In [19]: print Steve
Student(name='Steven Schultz', age=23, major='English')
Roland Smith
  • 42,427
  • 3
  • 64
  • 94
  • not nitpicking, but the question was about OOP in python and your answer is about avoiding aspects of classes, or using advance methods such as casting to string. – WeaselFox Apr 22 '14 at 13:22
  • @WeaselFox Using `__str__` is not "advanced". It's *pythonic*, as in "There should be one-- and preferably only one --obvious way to do it." And OOP is not a be-all and end-all. There are many instances where it is not appropriate. See Jack Diederich's excellent video ["Stop Writing Classes"](http://www.youtube.com/watch?v=o9pEzgHorH0). – Roland Smith Apr 22 '14 at 13:57
  • I agree with what you say, but in the context of the question its not really relevant IMHO. The OP stated he's trying to learn OOP in python, and `__str__` is not the first thing you would start with. – WeaselFox Apr 23 '14 at 06:06