Learning LibraryCore Coding LibraryTeens

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

1The basics

Attributes are read and assigned with a dot. A method is called with a dot and parentheses:

python
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%}")
Run it. One attribute, one method:
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.

2A step further

A method can take arguments besides self, and it can change the object's own attributes:

python
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)
Run it. Selling changes the object's state:
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.

3At Evergreen

A method can also protect the object. This sell() never lets stock go negative, and it returns how many were really sold:

python
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():,}")
Run it. An order bigger than the stock:
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.

The same idea, everywhere

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.