Update page 'Day 5&6 Homework - Karen'
@@ -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)
|
|
||||||
```
|
|
||||||
@@ -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"]
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user