How can I define a class in Python?
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:
I declared that
Team
inherits fromobject
. 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.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
andteam3
. These arguments are named, so you can either provide values as positional parameters (as withteam
) or you can use theargument=
form as I did withteam3
. When you explicitly specify the name of the arguments, you can specify arguments in any order.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/
- 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.
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).