{"id":"encapsulation-private","slug":"private-attributes","title":"Private attributes","text":"The code that creates objects of a class and uses them is called the class's **client**. A well-made class keeps its objects in a sensible state whatever the client does: a thermostat should never be set to 45 degrees, and a bank balance shouldn't change except by deposits and withdrawals. Keeping the attributes of an object acceptable at all times is called keeping its **integrity**.\n\nA method can check a value before storing it. But as long as the attribute itself is in reach, the client can skip the method and change the attribute directly:\n\n```python\nclass Thermostat:\n    def __init__(self):\n        self.target = 20\n\n    def set_target(self, degrees):\n        if 5 <= degrees <= 30:\n            self.target = degrees\n\n\nheater = Thermostat()\nheater.set_target(45)\nprint(heater.target)\nheater.target = 45\nprint(heater.target)\n```\n\n```output\n20\n45\n```\n\nThe method refused 45, and then the client set it anyway.\n\n### Two underscores make it private\n\nAn attribute whose name starts with two underscores, such as `__target`, is **private**: only the class's own methods can use it by that name. Code outside the class that tries to read it gets an error. To let the client see the value, the class offers a method that returns it:\n\n```python\nclass Thermostat:\n    def __init__(self):\n        self.__target = 20\n\n    def set_target(self, degrees):\n        if 5 <= degrees <= 30:\n            self.__target = degrees\n\n    def get_target(self):\n        return self.__target\n\n\nheater = Thermostat()\nheater.set_target(22)\nprint(heater.get_target())\nprint(heater.__target)\n```\n\n```output\n22\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 16, in <module>\n    print(heater.__target)\n          ^^^^^^^^^^^^^^^\nAttributeError: 'Thermostat' object has no attribute '__target'. Did you mean: 'get_target'?\n```\n\nInside the class, `self.__target` works as usual. Outside it, the attribute can't be reached by that name (Python even suggests the public method to use instead), so the only way to change the target is `set_target`, which checks the value.\n\nHiding an object's attributes and offering methods to use them is called **encapsulation**. The client tells the object what to do, and the object's own methods decide how. That also leaves the class free to change: if `Thermostat` later kept its target in tenths of a degree, only its methods would need rewriting, and every program that calls `set_target` and `get_target` would go on working.\n\n### Private, not secret\n\nOne thing outside code can do without an error is assign to the private name. But that doesn't reach the private attribute:\n\n```python\nclass Thermostat:\n    def __init__(self):\n        self.__target = 20\n\n    def get_target(self):\n        return self.__target\n\n\nheater = Thermostat()\nheater.__target = 45\nprint(heater.__target)\nprint(heater.get_target())\nprint(heater._Thermostat__target)\n```\n\n```output\n45\n20\n20\n```\n\n`heater.__target = 45` gave the object a new, unrelated attribute that happens to be called `__target`, and the thermostat's real setting stayed at 20. The last line shows why: inside a class, Python quietly renames every private name, putting an underscore and the class's name in front. So the real attribute is `_Thermostat__target`, and only code inside the class uses it by its short name.\n\nThat renaming is all \"private\" means in Python. Anyone who writes out the long name can still reach the attribute, so the two underscores aren't a lock but a label: this belongs to the class, use its methods. Treat it that way, and never write a name like `_Thermostat__target` in your own programs.\n\nThe two underscores go only at the start of the name. A name with two underscores at both ends, like `__init__` or `__str__`, is one of Python's special methods, and it isn't private.\n","task":"Make the counter's attribute private. Fill in the blanks so that the attribute is called `__count`, and the program prints:\n\n```\n3\n```\n","kind":"fill","seed":"class Counter:\n    def __init__(self):\n        self.[[__count]] = 0\n\n    def add(self):\n        self.__count += 1\n\n    def value(self):\n        return self.[[__count]]\n\n\nclicks = Counter()\nclicks.add()\nclicks.add()\nclicks.add()\nprint(clicks.value())\n","solution":"class Counter:\n    def __init__(self):\n        self.__count = 0\n\n    def add(self):\n        self.__count += 1\n\n    def value(self):\n        return self.__count\n\n\nclicks = Counter()\nclicks.add()\nclicks.add()\nclicks.add()\nprint(clicks.value())\n","hints":["The attribute is used in all three methods, and its name must be the same in each of them.","A private attribute's name starts with two underscores: `self.__count`."],"tests":[{"name":"Output","stdin":"","expect":"3","hidden":false}],"require":[],"forbid":[],"more":[{"task":"A travel card holds money for bus trips. Write the class `TravelCard` with two private attributes, `__balance` and `__trips`, both 0 when a card is created, and these methods:\n\n- `load(self, amount)` adds the amount to the balance, but only if it's more than 0.\n- `travel(self, price)` pays for a trip: if the balance is at least the price, it takes the price off the balance, adds one to the trips and returns `True`. Otherwise nothing changes and it returns `False`.\n- `__str__(self)` returns a string such as `balance 7.5, trips 1`.\n\nFor example, this code:\n\n```python\ncard = TravelCard()\ncard.load(10)\ncard.load(-50)\nprint(card.travel(2.5))\nprint(card.travel(9))\nprint(card)\n```\n\nprints:\n\n```\nTrue\nFalse\nbalance 7.5, trips 1\n```\n","kind":"code","seed":"class TravelCard:\n    pass\n\n\nif __name__ == \"__main__\":\n    card = TravelCard()\n    card.load(10)\n    card.load(-50)\n    print(card.travel(2.5))\n    print(card.travel(9))\n    print(card)\n","solution":"class TravelCard:\n    def __init__(self):\n        self.__balance = 0\n        self.__trips = 0\n\n    def load(self, amount):\n        if amount > 0:\n            self.__balance += amount\n\n    def travel(self, price):\n        if self.__balance < price:\n            return False\n        self.__balance -= price\n        self.__trips += 1\n        return True\n\n    def __str__(self):\n        return f\"balance {self.__balance}, trips {self.__trips}\"\n\n\nif __name__ == \"__main__\":\n    card = TravelCard()\n    card.load(10)\n    card.load(-50)\n    print(card.travel(2.5))\n    print(card.travel(9))\n    print(card)\n","hints":["Create both attributes in `__init__` with two underscores at the start of their names: `self.__balance = 0`.","`load` only changes the balance when `amount > 0`.","In `travel`, return `False` first if `self.__balance < price`; otherwise change both attributes and return `True`."],"tests":[{"name":"Test 1","stdin":"","call":"card = TravelCard()\ncard.load(10)\ncard.load(-50)\nprint(card.travel(2.5))\nprint(card.travel(9))\nprint(card)\n","expect":"True\nFalse\nbalance 7.5, trips 1","hidden":false},{"name":"Test 2","stdin":"","call":"card = TravelCard()\nprint(card.travel(1))\ncard.load(6)\ncard.load(0)\nprint(card.travel(3))\nprint(card.travel(3))\nprint(card.travel(3))\nprint(card)\n","expect":"False\nTrue\nTrue\nFalse\nbalance 0, trips 2","hidden":true}],"require":[{"pattern":"self\\.__balance\\b","message":"Keep the balance in a private attribute, `self.__balance`."},{"pattern":"self\\.__trips\\b","message":"Keep the number of trips in a private attribute, `self.__trips`."}],"forbid":[]},{"task":"A shop serves customers in the order they arrive. Write the class `Queue`, which keeps the names of the waiting customers in a private list `__names`:\n\n- `join(self, name)` adds a name to the end of the queue.\n- `serve(self)` removes the first name from the queue and returns it. If the queue is empty, it returns `None`.\n- `length(self)` returns the number of people waiting.\n\nFor example, this code:\n\n```python\nqueue = Queue()\nqueue.join(\"Ada\")\nqueue.join(\"Ben\")\nprint(queue.serve())\nprint(queue.length())\nprint(queue.serve())\nprint(queue.serve())\n```\n\nprints:\n\n```\nAda\n1\nBen\nNone\n```\n","kind":"code","seed":"class Queue:\n    pass\n\n\nif __name__ == \"__main__\":\n    queue = Queue()\n    queue.join(\"Ada\")\n    queue.join(\"Ben\")\n    print(queue.serve())\n    print(queue.length())\n    print(queue.serve())\n    print(queue.serve())\n","solution":"class Queue:\n    def __init__(self):\n        self.__names = []\n\n    def join(self, name):\n        self.__names.append(name)\n\n    def serve(self):\n        if len(self.__names) == 0:\n            return None\n        return self.__names.pop(0)\n\n    def length(self):\n        return len(self.__names)\n\n\nif __name__ == \"__main__\":\n    queue = Queue()\n    queue.join(\"Ada\")\n    queue.join(\"Ben\")\n    print(queue.serve())\n    print(queue.length())\n    print(queue.serve())\n    print(queue.serve())\n","hints":["Create the private list in `__init__`: `self.__names = []`.","`pop(0)` removes the first item of a list and returns it.","Check for an empty list first in `serve`, and return `None` in that case."],"tests":[{"name":"Test 1","stdin":"","call":"queue = Queue()\nqueue.join(\"Ada\")\nqueue.join(\"Ben\")\nprint(queue.serve())\nprint(queue.length())\nprint(queue.serve())\nprint(queue.serve())\n","expect":"Ada\n1\nBen\nNone","hidden":false},{"name":"Test 2","stdin":"","call":"queue = Queue()\nprint(queue.length())\nprint(queue.serve())\nfor name in [\"Kim\", \"Lou\", \"Max\"]:\n    queue.join(name)\nprint(queue.length())\nprint(queue.serve())\nqueue.join(\"Noa\")\nprint(queue.serve(), queue.serve(), queue.serve())\nprint(queue.length())\n","expect":"0\nNone\n3\nKim\nLou Max Noa\n0","hidden":true}],"require":[{"pattern":"self\\.__names\\b","message":"Keep the names in a private list, `self.__names`."}],"forbid":[]}]}