{"id":"class-attributes-variables","slug":"class-variables","title":"Class variables","text":"So far every object has had its own values for its attributes: two products, two prices. Sometimes a value belongs to the class as a whole and is shared by all its objects, such as a tax rate that's the same for every product. That's a **class variable**. You write it inside the class but outside any method, without `self.`:\n\n```python\nclass Product:\n    tax_percent = 24\n\n    def __init__(self, name, price):\n        self.name = name\n        self.price = price\n\n    def total_price(self):\n        return self.price + self.price * Product.tax_percent / 100\n\n\nprint(\"Tax:\", Product.tax_percent, \"%\")\nbread = Product(\"Bread\", 4)\nlamp = Product(\"Lamp\", 50)\nprint(bread.total_price(), lamp.total_price())\n\nProduct.tax_percent = 10\nprint(bread.total_price(), lamp.total_price())\n```\n\n```output\nTax: 24 %\n4.96 62.0\n4.4 55.0\n```\n\nYou reach a class variable through the class's name: `Product.tax_percent`. It exists as soon as the class does, before any object is created, which is why the first `print` works. There is only one `tax_percent`, so changing it once changes the total price of every product.\n\nAttributes that each object has for itself, like `self.price`, are called **instance variables** (an object is an *instance* of its class). Any value can be a class variable, a list or a dictionary too.\n\n### Counting objects\n\nA class variable can count how many objects have been created, and give each new object the next number:\n\n```python\nclass Ticket:\n    sold = 0\n\n    def __init__(self, show):\n        Ticket.sold += 1\n        self.number = Ticket.sold\n        self.show = show\n\n    def __str__(self):\n        return f\"Ticket {self.number}: {self.show}\"\n\n\na = Ticket(\"Hamlet\")\nb = Ticket(\"Hamlet\")\nc = Ticket(\"Cats\")\nprint(a)\nprint(c)\nprint(\"Tickets sold:\", Ticket.sold)\n```\n\n```output\nTicket 1: Hamlet\nTicket 3: Cats\nTickets sold: 3\n```\n\n`Ticket.sold` is shared, and `self.number` is each ticket's own copy of what it was at that moment.\n\n### Change it through the class\n\nInside a method, `self.sold` can *read* the class variable: when an object has no attribute of that name, Python looks in its class. But *assigning* to `self.sold` creates a new attribute in that one object, and the class variable doesn't change:\n\n```python\nclass Ticket:\n    sold = 0\n\n    def __init__(self):\n        self.sold += 1\n\n\na = Ticket()\nb = Ticket()\nprint(Ticket.sold, a.sold, b.sold)\n```\n\n```output\n0 1 1\n```\n\nEach ticket now has its own `sold`, and the shared count is still 0. To change a class variable, always go through the class's name: `Ticket.sold += 1`.\n","task":"Every new club member should get the next member number. Fill in the blanks so the program prints:\n\n```\n1 Ada\n2 Linus\n3 Grace\nMembers: 3\n```\n","kind":"fill","seed":"class Member:\n    count = 0\n\n    def __init__(self, name):\n        Member.[[count]] += 1\n        self.number = [[Member.count]]\n        self.name = name\n\n\nmembers = [Member(\"Ada\"), Member(\"Linus\"), Member(\"Grace\")]\nfor member in members:\n    print(member.number, member.name)\nprint(\"Members:\", Member.count)\n","solution":"class Member:\n    count = 0\n\n    def __init__(self, name):\n        Member.count += 1\n        self.number = Member.count\n        self.name = name\n\n\nmembers = [Member(\"Ada\"), Member(\"Linus\"), Member(\"Grace\")]\nfor member in members:\n    print(member.number, member.name)\nprint(\"Members:\", Member.count)\n","hints":["The shared counter is the class variable `count`, and you change it through the class's name.","First `Member.count += 1`, then give the new member the current count: `self.number = Member.count`."],"tests":[{"name":"Output","stdin":"","expect":"1 Ada\n2 Linus\n3 Grace\nMembers: 3","hidden":false}],"require":[],"forbid":[],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"class Lamp:\n    watts = 40\n\n    def __init__(self, room):\n        self.room = room\n\n    def describe(self):\n        return f\"{self.room}: {Lamp.watts} W\"\n\n\ndesk = Lamp(\"study\")\nhall = Lamp(\"hall\")\nprint(desk.describe())\nLamp.watts = 60\nprint(desk.describe())\nprint(hall.describe())\n","solution":"class Lamp:\n    watts = 40\n\n    def __init__(self, room):\n        self.room = room\n\n    def describe(self):\n        return f\"{self.room}: {Lamp.watts} W\"\n\n\ndesk = Lamp(\"study\")\nhall = Lamp(\"hall\")\nprint(desk.describe())\nLamp.watts = 60\nprint(desk.describe())\nprint(hall.describe())\n","hints":["`watts` is a class variable: there is only one, shared by both lamps.","After `Lamp.watts = 60`, every lamp's `describe` uses 60."],"tests":[{"name":"Output","stdin":"","expect":"study: 40 W\nstudy: 60 W\nhall: 60 W","hidden":false}],"require":[],"forbid":[],"lines":["study: 40 W","study: 60 W","hall: 60 W"]},{"task":"Write the class `Coin`. It has two class variables:\n\n- `values`, a dictionary of the value in cents of each kind of coin: `\"penny\"` is 1, `\"nickel\"` is 5, `\"dime\"` is 10 and `\"quarter\"` is 25;\n- `minted`, the number of coins created so far, at first 0.\n\nThe constructor takes the kind of coin (one of the keys of `values`), stores it in the attribute `kind`, and adds one to `minted`. The method `cents(self)` returns the coin's value from the dictionary.\n\nFor example, this code:\n\n```python\nprint(Coin.values[\"dime\"])\npurse = [Coin(\"quarter\"), Coin(\"penny\"), Coin(\"dime\")]\ntotal = 0\nfor coin in purse:\n    total += coin.cents()\nprint(total)\nprint(Coin.minted)\n```\n\nprints:\n\n```\n10\n36\n3\n```\n","kind":"code","seed":"class Coin:\n    pass\n\n\nif __name__ == \"__main__\":\n    print(Coin.values[\"dime\"])\n    purse = [Coin(\"quarter\"), Coin(\"penny\"), Coin(\"dime\")]\n    total = 0\n    for coin in purse:\n        total += coin.cents()\n    print(total)\n    print(Coin.minted)\n","solution":"class Coin:\n    values = {\"penny\": 1, \"nickel\": 5, \"dime\": 10, \"quarter\": 25}\n    minted = 0\n\n    def __init__(self, kind):\n        self.kind = kind\n        Coin.minted += 1\n\n    def cents(self):\n        return Coin.values[self.kind]\n\n\nif __name__ == \"__main__\":\n    print(Coin.values[\"dime\"])\n    purse = [Coin(\"quarter\"), Coin(\"penny\"), Coin(\"dime\")]\n    total = 0\n    for coin in purse:\n        total += coin.cents()\n    print(total)\n    print(Coin.minted)\n","hints":["Both class variables go at the top of the class, outside any method: `values = {...}` and `minted = 0`.","In `__init__`, add one with the class's name: `Coin.minted += 1`.","`cents` looks up the coin's kind in the shared dictionary: `Coin.values[self.kind]`."],"tests":[{"name":"Test 1","stdin":"","call":"print(Coin.values[\"dime\"])\npurse = [Coin(\"quarter\"), Coin(\"penny\"), Coin(\"dime\")]\ntotal = 0\nfor coin in purse:\n    total += coin.cents()\nprint(total)\nprint(Coin.minted)\n","expect":"10\n36\n3","hidden":false},{"name":"Test 2","stdin":"","call":"print(Coin.minted)\nprint(Coin(\"nickel\").cents())\nprint(Coin(\"quarter\").cents() + Coin(\"quarter\").cents())\nprint(Coin.minted)\nprint(Coin.values[\"penny\"], Coin(\"penny\").kind)\n","expect":"0\n5\n50\n3\n1 penny","hidden":true}],"require":[],"forbid":[]}]}