diff --git a/max submission/6_15_HW.py b/max submission/6_15_HW.py new file mode 100644 index 0000000..3fe3621 --- /dev/null +++ b/max submission/6_15_HW.py @@ -0,0 +1,165 @@ +class BananaContextManager: + def __init__(self, banana_list): + self.banana_list = banana_list + + def __enter__(self): + self.banana_list.append("🍌") # Add a banana to the list + return self + + def __exit__(self, exc_type, exc_value, traceback): + if self.banana_list: # Check if the list is not empty + self.banana_list.pop() # Remove a banana from the list +my_banana_list = [] +with BananaContextManager(my_banana_list): + print(my_banana_list) +print(my_banana_list) # Output: [] + +#Questions: can you use a with block inside of another with block? +class BananaContextManager: + def __init__(self, banana_list): + self.banana_list = banana_list + + def __enter__(self): + self.banana_list.append("🍌") # Add a banana to the list + return self + + def __exit__(self, exc_type, exc_value, traceback): + if self.banana_list: # Check if the list is not empty + self.banana_list.pop() # Remove a banana from the list +my_banana_list = [] +with BananaContextManager(my_banana_list) as outer_cm: + print(my_banana_list) + + with BananaContextManager(outer_cm.banana_list) as inner_cm: + inner_cm.banana_list.append("🍌") # Add another banana to the list + print(my_banana_list) + + inner_cm.banana_list.pop() # Remove a banana from the list + print(my_banana_list) + + print(my_banana_list) + +print(my_banana_list) +#The number of bananas in the list are 1,3,2,1,0. + +#question:can you structure the program so that you can get the class to have more than 0 bananas *outside* of a with block? +class BananaContextManager: + def __init__(self, banana_list, initial_bananas=0): + self.banana_list = banana_list + self.initial_bananas = initial_bananas + + def __enter__(self): + for _ in range(self.initial_bananas): + self.banana_list.append("🍌") + self.banana_list.append("🍌") # Add a banana to the list + return self + + def __exit__(self, exc_type, exc_value, traceback): + if self.banana_list: # Check if the list is not empty + self.banana_list.pop() # Remove a banana from the list + +# Usage example +my_banana_list = [] +banana_count = 1 # Number of bananas outside the with block + +with BananaContextManager(my_banana_list, banana_count) as outer_cm: + print(my_banana_list) + + with BananaContextManager(outer_cm.banana_list) as inner_cm: + inner_cm.banana_list.append("🍌") # Add another banana to the list + print(my_banana_list) + + inner_cm.banana_list.pop() # Remove a banana from the list + print(my_banana_list) + + print(my_banana_list) + +print(my_banana_list) + +#Update notes: +#salting: +#Let's say we have two users, Alice and Bob, who both have the same password, "password123". Without salting, +#if we were to hash their passwords directly, they would produce the same hash value: +''''' +Alice's Password: password123 +Bob's Password: password123 + +Hashed Password (Alice and Bob): 7c6a180b36896a0a8c02787eeafb0e4c7b07a52e +''''' +#As you can see, the hashed passwords for both Alice and Bob are the same. This could be a problem because if an attacker obtains the hash value, +#they can easily determine that Alice and Bob have the same password. +#Now, let's introduce salting to the process. Each user will have a unique salt that is added to their password before hashing. +# Here's an updated example: +''''' +Alice's Password: password123 +Bob's Password: password123 + +Alice's Salt: QGdG9jzV +Bob's Salt: d8K27fsE + +Salted and Hashed Password (Alice): QGdG9jzVpassword123 -> 48cf6b7b4b04819f6d717c9bc67a4feae0f1081a +Salted and Hashed Password (Bob): d8K27fsEpassword123 -> d5fd5b3a42f8e6bc8c9844feeff24f4cb4fd8fa4 + +''''' +#Now, even though Alice and Bob have the same password, their hashed passwords are different +#because they used different salts. The salt makes each hash unique, even when the passwords are the same. + +#Rich comparison methods: +#Rich comparison methods allow objects to be compared using operators like <, >, ==, etc. By implementing these +#methods in a class, you can define how objects of that class should be compared. Here's an example +#using the __eq__ method to compare two Point objects: +class Point: + def __init__(self, x, y): + self.x = x + self.y = y + + def __eq__(self, other): + return self.x == other.x and self.y == other.y + +point1 = Point(1, 2) +point2 = Point(1, 2) +point3 = Point(3, 4) + +print(point1 == point2) # Output: True +print(point1 == point3) # Output: False + +#Swapped-argument versions: +#Swapped-argument versions of methods provide flexibility when working with commutative operations. +#For example, if you have a class representing a mathematical operation and want to support swapping the operands, +#you can define a swapped-argument version of the method. + +#Hashed collections: +#Hashed collections like dictionaries (dict) and sets (set) use hash functions internally to efficiently store +#and retrieve data. Here's an example using a dictionary: +my_dict = {'apple': 5, 'banana': 3, 'orange': 2} + +print(my_dict['apple']) # Output: 5 +print(my_dict.get('banana')) # Output: 3 + +#Dictionary insertion: +#Dictionary insertion refers to adding key-value pairs to a dictionary. Here's an example: +my_dict = {'apple': 5, 'banana': 3} + +# Inserting a new key-value pair +my_dict['orange'] = 2 + +print(my_dict) # Output: {'apple': 5, 'banana': 3, 'orange': 2} + +#Multiple inheritance: +#Multiple inheritance allows a class to inherit from multiple base classes. Here's a simplified example: +class Base1: + def method1(self): + print("Base1 method") + +class Base2: + def method2(self): + print("Base2 method") + +class MyClass(Base1, Base2): + pass + +my_object = MyClass() +my_object.method1() # Output: Base1 method +my_object.method2() # Output: Base2 method + +#AI cannot give me practcial examples about Intentional asymmetry and Cryptography.