HW for 6_15
This commit is contained in:
@@ -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.
|
||||||
Reference in New Issue
Block a user