62

Quite simple, I'm learning Python, and I can't find a reference that tells me how to write the following:

public class Team {
     private String name;
     private String logo;
     private int members;

     public Team(){}

     // Getters/setters
}

Later:

Team team = new Team();
team.setName("Oscar");
team.setLogo("http://....");
team.setMembers(10);

That is a class Team with the properties: name/logo/members

Edit

After a few attempts I got this:

class Team:
    pass

Later

team = Team()
team.name = "Oscar"
team.logo = "http://..."
team.members = 10

Is this the Python way? It feels odd (coming from a strongly typed language of course).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • 1
    I'd like to suggest that someone change the title to something that is better for future searching. Something like "Best way to define a class in Python". – steveha Sep 30 '09 at 19:42
  • @steveha: Maybe the although my real question was, how do I write this in python... what about ... – OscarRyz Sep 30 '09 at 21:54

3 Answers3

120

Here is what I would recommend:

class Team(object):
    def __init__(self, name=None, logo=None, members=0):
        self.name = name
        self.logo = logo
        self.members = members

team = Team("Oscar", "http://...", 10)

team2 = Team()
team2.name = "Fred"

team3 = Team(name="Joe", members=10)

Some notes on this:

  1. I declared that Team inherits from object. This makes Team a "new-style class"; this has been recommended practice in Python since it was introduced in Python 2.2. (In Python 3.0 and above, classes are always "new-style" even if you leave out the (object) notation; but having that notation does no harm and makes the inheritance explicit.) Here's a Stack Overflow discussion of new-style classes.

  2. It's not required, but I made the initializer take optional arguments so that you can initialize the instance on one line, as I did with team and team3. These arguments are named, so you can either provide values as positional parameters (as with team) or you can use the argument= form as I did with team3. When you explicitly specify the name of the arguments, you can specify arguments in any order.

  3. If you needed to have getter and setter functions, perhaps to check something, in Python you can declare special method functions. This is what Martin v. Löwis meant when he said "property descriptors". In Python, it is generally considered good practice to simply assign to member variables, and simply reference them to fetch them, because you can always add in the property descriptors later should you need them. (And if you never need them, then your code is less cluttered and took you less time to write. Bonus!)

Here's a good link about property descriptors: http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/

Note: Adam Gomaa's blog seems to have disappeared from the web. Here's a link to a saved copy at archive.org:

https://web.archive.org/web/20160407103752/http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/

  1. It doesn't really matter if you specify values as part of the call to Team() or if you poke them into your class instance later. The final class instance you end up with will be identical.
team = Team("Joe", "http://example.com", 1)
team2 = Team()
team2.name = "Joe"
team2.logo = "http://example.com"
team2.members = 1

print(team.__dict__ == team2.__dict__)

The above will print True. (You can easily overload the == operator for Team instances, and make Python do the right thing when you say team == team2, but this doesn't happen by default.)


I left out one thing in the above answer. If you do the optional argument thing on the __init__() function, you need to be careful if you want to provide a "mutable" as an optional argument.

Integers and strings are "immutable". You can never change them in place; what happens instead is Python creates a new object and replaces the one you had before.

Lists and dictionaries are "mutable". You can keep the same object around forever, adding to it and deleting from it.

x = 3   # The name "x" is bound to an integer object with value 3
x += 1  # The name "x" is rebound to a different integer object with value 4

x = []  # The name "x" is bound to an empty list object
x.append(1)  # The 1 is appended to the same list x already had

The key thing you need to know: optional arguments are evaluated only once, when the function is compiled. So if you pass a mutable as an optional argument in the __init__() for your class, then each instance of your class shares one mutable object. This is almost never what you want.

class K(object):
    def __init__(self, lst=[]):
        self.lst = lst

k0 = K()
k1 = K()

k0.lst.append(1)

print(k0.lst)  # prints "[1]"
print(k1.lst)  # also prints "[1]"

k1.lst.append(2)

print(k0.lst)  # prints "[1, 2]"

The solution is very simple:

class K(object):
    def __init__(self, lst=None):
        if lst is None:
            self.lst = []  # Bind lst with a new, empty list
        else:
            self.lst = lst # Bind lst with the provided list

k0 = K()
k1 = K()

k0.lst.append(1)

print(k0.lst)  # prints "[1]"
print(k1.lst)  # prints "[]"

This business of using a default argument value of None, then testing that the argument passed is None, qualifies as a Python design pattern, or at least an idiom you should master.

steveha
  • 74,789
  • 21
  • 92
  • 117
  • And if you want the default values of the strings to be "" instead of None, you can just make that change in the __init__() function. – steveha Sep 30 '09 at 01:38
  • +1 Interesting: If the named parameters is used, I won't get the "method __init__ uses exactly 4 parameters used 0" error message?? ... – OscarRyz Sep 30 '09 at 01:56
  • @Oscar Reyes: no. If a default value is provided for them, those parameters are optional. – Cristian Ciupitu Sep 30 '09 at 02:15
  • 1
    I love the clear detailed discussion going way beyond the origional question. Beautiful thanks – GloryDev Nov 12 '17 at 21:17
  • The *adam.gomaa.us* link seems to be broken (domain does not exist?). – Peter Mortensen Feb 03 '20 at 18:23
  • I promise you that the Adam Gomaa blog existed in 2009 when I wrote this answer. I have edited the answer to include an archive.org link to a saved copy of this blog entry. – steveha Mar 10 '20 at 01:41
63
class Team:
  def __init__(self):
    self.name = None
    self.logo = None
    self.members = 0

In Python, you typically don't write getters and setters, unless you really have a non-trivial implementation for them (at which point you use property descriptors).

Martin v. Löwis
  • 124,830
  • 17
  • 198
  • 235
  • Martin, what's the difference between my finding **class Team:pass** and your sample? – OscarRyz Sep 30 '09 at 01:13
  • 2
    You can dynamically add attributes as you wish, but if the Team class will always contain those attributes it's clearer to define them within the class and not externally. – monkut Sep 30 '09 at 01:18
  • 1
    It's useful for documentation as it lets people looking at your class see which attributes are useful. – Colin Coghill Sep 30 '09 at 01:19
  • 3
    The main difference is that after `Team()`, you already have name, log, members set to some default values; for an empty class, you don't have any attributes set initially. You typically avoid adding attributes later and all set them in the constructor, so that you can trust that they are present in all methods. However, you also try to initialize them to their right values (from constructor arguments) if at all possible, just as you would do in Java. – Martin v. Löwis Sep 30 '09 at 01:19
  • @Pynt, how so? Looks fairly standard to me so if there's something people should avoid can you detail it a bit for us? – physicsmichael Oct 01 '09 at 04:12
  • @Pynt: depends on the Python version. In Python 3.1, this is a new-style class. – Martin v. Löwis Oct 01 '09 at 05:36
0

To write classes you would normally do:

class Person:
    def __init__(self, name, age, height):
        self.name = name
        self.age = age
        self.height = height

To instantiate instances of a class(es) you would do

person1 = Person("Oscar", 40, "6ft")

person2 = Team("Danny", 12, "5.2ft")

You can also set a default value:

 class Person:
        def __init__(self):
            self.name = "Daphne"
            self.age = 20
            self.height = "5.4ft"

To instantiate a classes set like this, you want to do:

person3 = Person()
person3.name = "Joe"
person3.age = 23
person3.height = "5.11ft"

You will notice that this method bears a lot of similarity to your typical Python dictionary interaction.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
maestro.inc
  • 796
  • 1
  • 6
  • 13