Objects, attributes, functions, and methods
Sunday, April 07, 2013
I set some time out to open the box on some Python fundamentals this weekend. I have a few topics to write about but, for today, let's talk a little about objects.
Objects
Objects are a big deal in Python. And rightfully so, as most developers use Python in an object oriented manner, though you can certainly write imperatively or pseudo-functionally too. In fact, some people subscribe to the thought that everything in Python is an object. I'm not going to argue about whether that's true or not so we'll just tip-toe around it and state that there are an awful lot of objects.
So what is an object? Let's use Wikipedia's definition. Objects are characterized by these properties:
Identity (Objects are unique from each other)
class Trivial(object):
pass
obj, diff_obj = Trivial(), Trivial()
id(obj) # 25851216
id(diff_obj) # 26133840
State (Can store data in object)
import os # modules are objects
os.my_data = 'arbitrary'
print os.my_data # arbitrary
Behavior (Can manipulate the object)
print ' '.join(['strings', 'are', 'objects', 'with', 'methods'])
# strings are objects with methods
Attributes and Methods
You're probably familiar with setting object attributes to static data or writing simple methods if you've worked with objects in the past.
class Lannister(object):
def __init__(self, name):
self.name = name
def say_family_motto(self):
print '{} says: "Hear Me Roar!"'.format(self.name)
tyrion = Lannister()
print tyrion.name
# Tyrion Lannister
tyrion.say_family_motto()
# Tyrion Lannister says: "Hear Me Roar!"
But you're actually not limited to such trivial behavior. For instance, since functions are first class objects in Python, you could easily do the following too.
def say_common_motto():
print 'A Lannister Always Pays His Debts.'
tyrion.say_common_motto = say_common_motto
tyrion.say_common_motto()
# A Lannister Always Pays His Debts.
As you can see, we've set the .say_common_motto
attribute to the function object say_common_motto
, then invoked the attribute.
Before we continue, we should make a distinction between .say_common_motto
and .say_family_motto
.
type(tyrion.say_common_motto)
# <type 'function'>
type(tyrion.say_family_motto)
# <type 'instancemethod'>
Python resolves the type of .say_common_motto
to a function object and .say_family_motto
to an instance method object. What's the difference?
You can think of a method as a function with a unique difference. A method always takes the object it's associated with as it's first argument. You don't have a choice on the matter.
Under the hood, when Python sees tyrion.say_family_motto()
, it first looks for a .say_family_motto
attribute in tyrion
(this means you can override an object method by writing to an object attribute with the same name). When it doesn't find that, the interpreter will jump up to the class level and look for say_family_motto
function defined at the class level. Once found, it will invoke the function as a method - namely, by attaching the tyrion
object to the head of the argument list. That's also why a method definition always includes a positional argument placeholder first (traditionally called self
in Python).
Methods are really just a shortcut though. You can actually just define your own function with an argument placeholder for an object and attach the function to the object attribute. An example makes this clearer. Suppose we rewrote the say_common_motto
function to make it equivalent to a method.
def say_common_motto(self):
print '{} says: "A Lannister Always Pays His Debts."'.format(self.name)
tyrion.say_common_motto = say_common_motto
tyrion.say_common_motto(tyrion)
# Tyrion Lannister says: "A Lannister Always Pays His Debts."
tyrion.say_family_motto()
# Tyrion Lannister says: "Hear Me Roar!"
As you can see, defining a method in the class is a lot cleaner than attaching a function to the instance object. But both styles get you to the same resolution eventually.
Anyways, hopefully you know a little more about Python objects now.