From 00a9b22589d4329b945f50cfa9075a84eec0788d Mon Sep 17 00:00:00 2001 From: Karenzz Date: Wed, 12 Apr 2023 00:38:30 +0200 Subject: [PATCH] Update page 'Day 5&6 Homework - Karen' --- Day 5 Homework - Karen.md | 30 ---------- Day 5&6 Homework - Karen.md | 116 ++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 30 deletions(-) delete mode 100644 Day 5 Homework - Karen.md create mode 100644 Day 5&6 Homework - Karen.md diff --git a/Day 5 Homework - Karen.md b/Day 5 Homework - Karen.md deleted file mode 100644 index 277d589..0000000 --- a/Day 5 Homework - Karen.md +++ /dev/null @@ -1,30 +0,0 @@ -**The assignment for tonight is to write a class named Vault containing an attribute named "treasure" which returns "gold" under normal circumstances, but a warning message such as "attribute deleted!" if it detects any of its attributes have been changed. You can use the descriptor protocol (__set__, etc) to detect if its attributes have been changed, and it will have to check its own __dict__ against a known good copy before accessing the treasure attribute to make sure that the attributes are what they're supposed to be.** - -``` -class Vault: - def __init__(self): - self._treasure = "gold" - self._original_dict = dict(self.__dict__) - - def __setattr__(self, name, value): - if name != "_treasure" and name in self.__dict__ and self._original_dict.get(name) != value: - print("attribute deleted!") - self.__dict__[name] = value - - @property - def treasure(self): - if self.__dict__ != self._original_dict: - print("attribute deleted!") - return self._treasure -v = Vault() -print(v.treasure) -``` -To assess other's Vault (although it may not be ethical), we can use `__getattribute__` method like: - -``` -class EvilVault(Vault): - def __getattribute__(self, name): - if name == "treasure": - return "gold" - return super().__getattribute__(name) -``` \ No newline at end of file diff --git a/Day 5&6 Homework - Karen.md b/Day 5&6 Homework - Karen.md new file mode 100644 index 0000000..8347dc1 --- /dev/null +++ b/Day 5&6 Homework - Karen.md @@ -0,0 +1,116 @@ +**The assignment for tonight is to write a class named Vault containing an attribute named "treasure" which returns "gold" under normal circumstances, but a warning message such as "attribute deleted!" if it detects any of its attributes have been changed. You can use the descriptor protocol (__set__, etc) to detect if its attributes have been changed, and it will have to check its own __dict__ against a known good copy before accessing the treasure attribute to make sure that the attributes are what they're supposed to be.** + +``` +class Vault: + def __init__(self): + self._treasure = "gold" + self._original_dict = dict(self.__dict__) + + def __setattr__(self, name, value): + if name != "_treasure" and name in self.__dict__ and self._original_dict.get(name) != value: + print("attribute deleted!") + self.__dict__[name] = value + + @property + def treasure(self): + if self.__dict__ != self._original_dict: + print("attribute deleted!") + return self._treasure +v = Vault() +print(v.treasure) +``` + +Here is another method using `__getattribute__` and `__setattr__`. + +``` +class Vault: + def __init__(self): + self._treasure = "gold" + self._known_good_copy = self.__dict__.copy() + + def __getattribute__(self, name): + if name == "treasure": + if self.__dict__ != self._known_good_copy: + print("Attribute deleted!") + return self._treasure + else: + return object.__getattribute__(self, name) + + def __setattr__(self, name, value): + if name != "_treasure" and name != "_known_good_copy": + print("Attribute deletion detected!") + self._known_good_copy = self.__dict__.copy() + object.__setattr__(self, name, value) +v = Vault() +print(v.treasure) +``` + +Also, we can define a descriptor by implementing the `__get__`, `__set__`, and `__delete__` methods. + +``` +class Vault: + def __init__(self): + self._treasure = "gold" + self._known_good_copy = self.__dict__.copy() + + def __get__(self, instance, owner): + if instance is None: + return self + else: + if instance.__dict__ != self._known_good_copy: + print("Attribute deleted!") + return self._treasure + + def __set__(self, instance, value): + print("Attribute deletion detected!") + self._known_good_copy = instance.__dict__.copy() + + def __delete__(self, instance): + print("Attribute deletion detected!") + self._known_good_copy = instance.__dict__.copy() + + def __set_name__(self, owner, name): + self.name = name +v = Vault() +print(v._treasure) +``` + +Unfortunately, I didn't figure out what happened to the code that I used `super().__setattr__` last week, so I create a question on stack overflow to see if someone can help me with this issue. +https://stackoverflow.com/questions/75990535/attributeerror-and-recursionerror-happened-when-using-setattr-in-python + +``` +class Vault: + def __init__(self): + self._treasure = "gold" + self._attributes = dict(self.__dict__) + + def __setattr__(self, name, value): + if name != "_attributes" and name not in self._attributes: + print("attribute deleted!") + self._attributes[name] = value + super().__setattr__("_attributes", dict(self.__dict__)) + + @property + def treasure(self): + if self.__dict__ != self._attributes: + print("attribute deleted!") + return self._treasure + +v = Vault() +print(v.treasure) +``` + +To assess other's Vault (although it may not be ethical), we can use `__getattribute__` method like: + +``` +class EvilVault(Vault): + def __getattribute__(self, name): + if name == "treasure": + return "gold" + return super().__getattribute__(name) +``` +Or I think we can use another easier way as accessing the attribute through the class's dictionary: + +``` +Vault.__dict__["_treasure"] +``` \ No newline at end of file