Methods and attributes in Python: what an object knows and does
An object has two sides. Attributes are what it knows: its price, its stock. Methods are what it can do: report a margin, record a sale. Good classes keep the two in step.
The big idea
An attribute is a value stored on an object; a method is a function that belongs to the object and works with its attributes through self.
See it in code
Attributes are read and assigned with a dot. A method is called with a dot and parentheses:
class Product:
def __init__(self, name, price, cost):
self.name = name
self.price = price
self.cost = cost
def margin(self):
return (self.price - self.cost) / self.price
tee = Product("Organic tee", 38, 14)
print("Attribute:", tee.price)
print(f"Method: {tee.margin():.1%}")Attribute: 38 Method: 63.2%
tee.price is stored data. tee.margin() is computed fresh each time from the current attributes, so it can never be out of date.
A method can take arguments besides self, and it can change the object's own attributes:
class Product:
def __init__(self, name, price, stock=0):
self.name = name
self.price = price
self.stock = stock
self.sold = 0
def sell(self, quantity):
self.stock = self.stock - quantity
self.sold = self.sold + quantity
tee = Product("Organic tee", 38, stock=12)
tee.sell(5)
print("Stock:", tee.stock, "| Sold:", tee.sold)Stock: 7 | Sold: 5
One call updated two attributes together. That's the value of a method: related changes happen in one place, and can't be half-done.
A method can also protect the object. This sell() never lets stock go negative, and it returns how many were really sold:
class Product:
def __init__(self, name, price, stock=0):
self.name = name
self.price = price
self.stock = stock
self.sold = 0
def sell(self, quantity):
quantity = min(quantity, self.stock)
self.stock = self.stock - quantity
self.sold = self.sold + quantity
return quantity
def revenue(self):
return self.sold * self.price
tee = Product("Organic tee", 38, stock=12)
shipped = tee.sell(20)
print(f"Asked for 20, shipped {shipped}")
print("Stock:", tee.stock, "| Sold:", tee.sold)
print(f"Revenue: ${tee.revenue():,}")Asked for 20, shipped 12 Stock: 0 | Sold: 12 Revenue: $456
The guard runs before the data changes, so stock stopped at 0 instead of falling to -8. Put the check after the subtraction and it would be too late.
You've used methods all along. "tee".upper(), orders.append(x), and stock.get(key, 0) are all methods on built-in objects. Writing a class lets you design your own.
Try it yourself
Add a restock(self, quantity) method and call it after the sale. Then add cost_per_wear(self, wears) returning self.price / wears, and call it with 100.
The common mistake
Calling a method without parentheses. tee.revenue refers to the method itself and prints something like <bound method ...>. To run it and get the value, write tee.revenue().
What it unlocks
Methods and attributes are the two halves of a class. A method is a function with one extra idea, self, and its return value works the same way.