9 Commits
9 changed files with 1133 additions and 1 deletions
@@ -0,0 +1,141 @@
{\rtf1\ansi\ansicpg1252\cocoartf2580
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red26\green26\blue26;\red255\green255\blue255;}
{\*\expandedcolortbl;;\cssrgb\c13333\c13333\c13333;\cssrgb\c100000\c100000\c100000;}
\margl1440\margr1440\vieww11520\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\parhyphenfactor20\partightenfactor0
\f0\fs32 \cf2 \cb3 \expnd0\expndtw0\kerning0
1. What is a type hierarchy?\
The type hierarchy in Python refers to the organization and relationships among different types or classes in the Python programming language. \
It defines how types are related to each other and how they inherit attributes and behaviors from their parent types.\
the type hierarchy in Python provides a structured organization of types, allowing for code reuse, inheritance, and polymorphism, which are fundamental principles of object-oriented programming.\
\
\
\
2. What is the difference between __repr__() and __str__()?\
The __repr__() method is intended to provide a detailed and unambiguous representation of the object. It is primarily used for debugging and development purposes.\
the __str__() method is used to provide a more user-friendly and readable representation of the object. It is intended for displaying the object to end-users.\
\
\
\
3. What is the range of an integer?\
In Python 2, the int type is a fixed-size integer, typically represented using 32 bits or 64 bits depending on the platform. The maximum value for an int in Python 2 is sys.maxint, which is usually 2^31 - 1 or 2^63 - 1. Or you need to use \'91Long\'92 instead.\
In Python 3, there is only one integer type, int, which behaves like the long type in Python 2. The int type in Python 3 can represent integers of arbitrary size limited only by the available memory.\
\
Ex:\
import sys\
\
# Python 2\
print(sys.maxint) # Output: 2147483647 (on most platforms)\
\
# Python 3\
print(sys.maxsize) # Output: 9223372036854775807 (on most platforms)\
\
\
\
4. Why string is a immutable sequence but you can still assign or change the index from a string?\
Strings are immutable sequences, which means that once a string is created, its elements cannot be modified. But you can still create new strings by using concatenation or string manipulation methods to achieve the desired modifications.\
\
\
\
5. Describe the __closure__ attribute.\
The __closure__ attribute returns a tuple of cell objects, where each cell object represents a captured variable. \
A cell object holds a reference to the variable's value, allowing the function to access and use the value even after the original scope has finished executing.\
The cell objects stored in the __closure__ attribute have the cell_contents attribute, which provides direct access to the value of the captured variable.\
\
\
\
6. What is a code object and a function object? Any difference between them?\
Code object:\
A code object represents byte-compiled executable Python code, or bytecode. \
It contains the instructions that make up the logic of a function, method, module, or other executable code. \
The code object includes information such as the bytecode instructions, the constants used by the bytecode, the names used by the bytecode, the filename from which the code was compiled, and more. \
Code objects are immutable and do not contain any references to mutable objects.\
Function object:\
A function object, on the other hand, is a callable object that represents a Python function. \
It contains a reference to a code object, which defines the implementation of the function. \
In addition to the code object, a function object also includes references to the function's globals (the module in which it was defined), the default argument values, and other attributes such as the function name, qualified function name, and more. \
Function objects are mutable and can have additional attributes assigned to them.\
\
In summary, a code object represents the bytecode and metadata of Python code, while a function object is a callable object that encapsulates a code object and provides additional functionality, such as the ability to be called and have attributes assigned to it. A function object is essentially a runtime representation of a function defined in Python code, with the code object containing the actual implementation details.\
\
\
\
7. What is a traceback object and how we use it?\
Traceback objects represent a stack trace of an exception. \
They are created when an exception occurs and provide information about the sequence of function calls leading up to the exception.\
\
One way to use it we can figure it out by using \'91try\'85 except\'85\'92 function:\
\
try:\
# Some code that may raise an exception\
except Exception as e:\
# Handle the exception\
print("An exception occurred:", type(e).__name__)\
print("Error message:", str(e))\
\
Exception is the base class for all built-in exceptions in Python. \
It is a superclass that encompasses a wide range of exceptions, such as TypeError, ValueError, NameError, and many others. \
By using Exception in the except statement, you catch any exception that is raised during the execution of the try block.\
\
\
\
8. What is the \'91auditing event\'92 meaning?\
It refers to a mechanism in Python that allows custom auditing or monitoring of attribute access on objects. When accessing the tb_frame attribute of a traceback object, an auditing event of type __getattr__ is raised. This event provides an opportunity to intercept and customize the attribute access behavior, if desired.\
\
In simple terms, an auditing event is a way to track or log attribute access on objects for monitoring or auditing purposes. It allows you to observe and potentially modify the behavior when accessing specific attributes.\
\
The auditing events are specific to the implementation of Python and are typically used in specialized scenarios where attribute access needs to be monitored or customized.\
\
\
\
9. How to find the common values and different values from two lists?\
list1 = [1, 2, 3, 4, 6]\
list2 = [2, 4, 6, 7, 8]\
\
# Find common values\
common_values = []\
for item in list1:\
if item in list2:\
common_values.append(item)\
\
# Find different values\
different_values = []\
for item in list1:\
if item not in list2:\
different_values.append(item)\
for item in list2:\
if item not in list1:\
different_values.append(item)\
\
print("Common values:", common_values) # Common values: [2, 4, 6]\
print("Different values:", different_values) # Different values: [1, 3, 7, 8]\
\
\
\
# Or there is another way to do this by using set() method\
# Find common values\
common_values = set(list1) & set(list2) # The '&' operator performs an intersection\
\
# Find different values\
different_values = set(list1) ^ set(list2) # The '^' operator performs a symmetric difference\
\
print("Common values:", common_values) # Common values: \{2, 4, 6\}\
print("Different values:", different_values) # Different values: \{1, 3, 7, 8\}\
\
\
\
10. The difference between list and set?\
In summary, lists are ordered, allow duplicate elements, and are mutable. \
Sets are unordered, do not allow duplicates, and are also mutable. \
\
The following detailed explanations are provided by AI:\
1. Order: Lists are ordered collections, meaning they preserve the order of elements. Each element in a list has an index associated with it. Sets, on the other hand, are unordered collections. They do not preserve the original order of elements and do not have indices.\
2. Duplicate elements: Lists allow duplicate elements, which means you can have multiple occurrences of the same value in a list. Sets, on the other hand, do not allow duplicate elements. Each element in a set is unique.\
3. Mutable vs. Immutable: Lists are mutable, which means you can modify individual elements, add new elements, or remove elements from a list. Sets are also mutable, allowing the addition and removal of elements. However, you cannot modify individual elements in a set since they are unordered.\
4. Membership testing: Checking for membership (whether an element exists in a collection) is faster in sets compared to lists. Sets use a hashing mechanism that provides efficient membership testing, while lists require iterating over the elements to check for membership.\
5. Mathematical set operations: Sets support various set operations like union, intersection, difference, and symmetric difference. These operations can be performed using operators (|, &, -, ^) or methods. Lists do not provide built-in operations for set operations.\
6. Data uniqueness: Sets are often used to ensure the uniqueness of elements in a collection. You can easily eliminate duplicates from a list by converting it to a set and then back to a list if the order of elements is not important.\
}
@@ -4,7 +4,7 @@
}
{\*\expandedcolortbl;;\cssrgb\c13333\c13333\c13333;\cssrgb\c100000\c100000\c100000;\cssrgb\c20392\c20784\c25490;
}
\margl1440\margr1440\vieww28300\viewh17700\viewkind0
\margl1440\margr1440\vieww28300\viewh14980\viewkind0
\deftab720
\pard\pardeftab720\parhyphenfactor20\partightenfactor0
@@ -56,6 +56,7 @@ What is a Von Neumann\'92s model of a \'93stored program computer\'94?\
\
\
\
\
\pard\pardeftab720\partightenfactor0
\cf4 \cb1 What is the difference between identity and type?\
Identity: \
+156
View File
@@ -0,0 +1,156 @@
# 3.3 Reading Note
They have same output:
```Python
class MyClass:
def __getitem__(self, index):
return f"Item at index {index}"
x = MyClass()
item = x[3]
print(item) # Output: Item at index 3
```
```Python
# Equivalent to type(x).\_\_getitem\_\_(x, 3)
item = type(x).__getitem__(x, 3)
print(item) # Output: Item at index 3
```
---
## 3.3.1
1. The object.\_\_new\_\_() method is a special method in Python that is called to create a new instance of a class. It is responsible for the low-level creation of an object before its initialization.
2. The object.\_\_init\_\_() method is a special method in Python that is called after an instance of a class has been created by the \_\_new\_\_() method. It is responsible for initializing the object's attributes and performing any necessary setup.
3. The object.\_\_del\_\_() method is a special method in Python that is called when an instance of a class is about to be destroyed or garbage-collected.
4. The cyclic garbage collector in CPython is responsible for detecting and breaking reference cycles, ensuring that memory is properly deallocated even in the presence of reference cycles.
5. the \_\_format\_\_() method allows objects to define their own customized string representations when used with format-related operations.
6. x < y calls x.\_\_lt\_\_(y)
x <= y calls x.\_\_le\_\_(y)
x == y calls x.\_\_eq\_\_(y)
x != y calls x.\_\_ne\_\_(y)
x > y calls x.\_\_gt\_\_(y)
x >= y calls x.\_\_ge\_\_(y)
---
## 3.3.2
### 3.3.2.1
1. The methods \_\_get\_\_, \_\_set\_\_, and \_\_delete\_\_ are used to implement descriptors in Python. Descriptors are a way to customize attribute access in classes. These methods are defined in a descriptor class, and when an instance of the descriptor class is present in an owner class, they are invoked to control attribute access.
### 3.3.2.2
1. In Python, a descriptor is an object attribute that has "binding behavior" due to the overriding of attribute access through methods in the descriptor protocol: \_\_get\_\_(), \_\_set\_\_(), and \_\_delete\_\_(). If an object defines any of these methods, it is considered a descriptor.
### 3.3.2.4
1. Using \_\_slots\_\_ can save significant memory compared to using \_\_dict\_\_, and it can also improve attribute lookup speed.
---
## 3.3.3
1.
``` Python
class Philosopher:
def __init_subclass__(cls, /, default_name, **kwargs):
super().__init_subclass__(**kwargs)
cls.default_name = default_name
class AustralianPhilosopher(Philosopher, default_name="Bruce"):
pass
```
This code sets up a base class called Philosopher that defines a custom behavior for its subclasses when they are created.
The default_name parameter allows each subclass to have a default name attribute, and the AustralianPhilosopher subclass demonstrates the usage by setting its default_name to "Bruce".
1. The purpose of \_\_set_name\_\_() is to provide a hook for objects to perform any necessary initialization or setup when they are assigned to a class attribute. It allows the objects to be aware of their assigned name within the class they belong to, which can be useful for various purposes such as introspection or customization.
### 3.3.3.1
1. The C3 linearization algorithm is a method used to determine the order in which base classes are searched for a particular attribute or method during multiple inheritance in Python. It ensures that the resolution order respects the consistency and priority of the inheritance hierarchy.
### 3.3.3.2
1. MRO (Method Resolution Order) entries are resolved: This refers to the determination of the order in which base classes are searched for a particular attribute or method. The MRO defines the inheritance hierarchy and is calculated using the C3 linearization algorithm.
---
## 3.3.5
1. Type annotations in Python refer to the practice of adding hints or metadata about the types of variables, function parameters, and return values in code. They are used to provide static type checking and improve code clarity, maintainability, and tooling support. They are optional and do not affect the runtime behavior of the code. Python remains a dynamically typed language, but type annotations allow developers to specify the expected types and enable type checking tools to catch potential errors.
``` Python
x: int = 5
def add(a: int, b: int) -> int:
return a + b
```
### 3.3.5.1
1. The purpose of \_\_class_getitem\_\_() is to allow runtime parameterization of standard-library generic classes in order to more easily apply type hints to these classes.
The \_\_class_getitem\_\_() method is called when you use square brackets notation ([]) with a generic class to specify the type arguments. For example:
```Python
from typing import List
# Define a generic class
class MyGenericClass:
...
# Use the generic class with type arguments
my_instance = MyGenericClass[int]()
```
In Python, generic classes are classes that can operate on different types of objects or collections in a flexible way. They are typically defined with type variables that represent the types of the objects or collections they work with. However, when using these generic classes, you need a way to specify the actual types you want to use.
In the example above, 'MyGenericClass[int]()' invokes the \_\_class_getitem\_\_() method of MyGenericClass to specify that the type argument for the generic class is int. This allows you to parameterize the generic class at runtime and apply type hints to it.
By implementing \_\_class_getitem\_\_() in your custom classes, you can enable similar behavior and allow runtime parameterization of your own generic classes.
2. What is the runtime parameterization?
**Runtime parameterization** refers to the ability to provide or specify parameters or arguments at runtime, rather than at compile time. It allows for dynamic customization or configuration of a program or component during its execution.
In the context of generic classes in Python, **runtime parameterization** refers to the ability to specify type arguments or type parameters for a generic class at runtime. Generic classes are designed to work with different types of objects or collections in a flexible way, and the specific types they operate on can be determined dynamically.
By allowing **runtime parameterization** of generic classes, you can provide the actual types or type arguments to be used when creating instances of the generic class. This enables you to customize the behavior or functionality of the generic class based on the specific types you need to work with in a particular situation.
**Runtime parameterization** of generic classes is commonly used in scenarios where the types involved may vary or depend on external factors. It provides flexibility and adaptability to handle different data types or collections dynamically, without requiring explicit type annotations or definitions at compile time.
---
## 3.3.9
In this example, we define a 'FileManager' class that acts as a context manager for working with files.
```Python
class FileManager:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.file = open(self.filename, 'w')
return self.file
def __exit__(self, exc_type, exc_value, traceback):
self.file.close()
# Usage of the context manager
with FileManager('example.txt') as file:
file.write('Hello, world!')
```
1. The *'\_\_enter\_\_'* method is responsible for setting up the runtime context, which in this case involves opening the specified file in write mode. The method returns the file object, which is then bound to the variable *'file'* in the *'with'* statement.
2. The *'\_\_exit\_\_'* method is called when exiting the runtime context. It is responsible for cleaning up any resources, in this case, closing the file. If an exception occurred within the *'with'* block, the exception information is passed to the *'\_\_exit\_\_'* method, allowing you to handle any necessary cleanup or error handling.
3. By using the *'with'* statement, the context manager automatically takes care of opening and closing the file for us. This ensures that the file is always closed, even if an exception occurs within the *'with'* block.
4. An explanation of the parameters in the *'\_\_exit\_\_'* method:
**self**: The self parameter refers to the instance of the class itself. It allows you to access the attributes and methods of the class within the '\_\_exit\_\_' method.
**exc_type**: This parameter represents the type of exception that occurred, if any. It will be None if no exception occurred within the with block.
**exc_value**: This parameter holds the instance of the exception that occurred, if any. Like exc_type, it will be None if no exception occurred.
**traceback**: The traceback parameter is a traceback object that encapsulates the call stack at the point where the exception occurred. It provides information about the line numbers and file names leading up to the exception.
+68
View File
@@ -0,0 +1,68 @@
{\rtf1\ansi\ansicpg1252\cocoartf2580
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\margl1440\margr1440\vieww28300\viewh17700\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0
\f0\fs24 \cf0 1. They have same output:\
class MyClass:\
def __getitem__(self, index):\
return f"Item at index \{index\}"\
\
x = MyClass()\
item = x[3]\
print(item) # Output: Item at index 3\
\
# Equivalent to type(x).__getitem__(x, 3)\
item = type(x).__getitem__(x, 3)\
print(item) # Output: Item at index 3\
\
2. The object.__new__() method is a special method in Python that is called to create a new instance of a class. It is responsible for the low-level creation of an object before its initialization. \
\
3. The object.__init__() method is a special method in Python that is called after an instance of a class has been created by the __new__() method. It is responsible for initializing the object's attributes and performing any necessary setup.\
\
4. The object.__del__() method is a special method in Python that is called when an instance of a class is about to be destroyed or garbage-collected.\
\
5. The cyclic garbage collector in CPython is responsible for detecting and breaking reference cycles, ensuring that memory is properly deallocated even in the presence of reference cycles.\
\
6. the __format__() method allows objects to define their own customized string representations when used with format-related operations.\
\
7. x < y calls x.__lt__(y)\
x <= y calls x.__le__(y)\
x == y calls x.__eq__(y)\
x != y calls x.__ne__(y)\
x > y calls x.__gt__(y)\
x >= y calls x.__ge__(y)\
\
8. The methods __get__, __set__, and __delete__ are used to implement descriptors in Python. Descriptors are a way to customize attribute access in classes. These methods are defined in a descriptor class, and when an instance of the descriptor class is present in an owner class, they are invoked to control attribute access.\
\
9. In Python, a descriptor is an object attribute that has "binding behavior" due to the overriding of attribute access through methods in the descriptor protocol: __get__(), __set__(), and __delete__(). If an object defines any of these methods, it is considered a descriptor.\
\
10. Using __slots__ can save significant memory compared to using __dict__, and it can also improve attribute lookup speed.\
\
11. class Philosopher:\
def __init_subclass__(cls, /, default_name, **kwargs):\
super().__init_subclass__(**kwargs)\
cls.default_name = default_name\
\
class AustralianPhilosopher(Philosopher, default_name="Bruce"):\
pass\
\
\
This code sets up a base class called Philosopher that defines a custom behavior for its subclasses when they are created. \
The default_name parameter allows each subclass to have a default name attribute, and the AustralianPhilosopher subclass demonstrates the usage by setting its default_name to "Bruce".\
\
12. The purpose of __set_name__() is to provide a hook for objects to perform any necessary initialization or setup when they are assigned to a class attribute. It allows the objects to be aware of their assigned name within the class they belong to, which can be useful for various purposes such as introspection or customization.\
\
13. The C3 linearization algorithm is a method used to determine the order in which base classes are searched for a particular attribute or method during multiple inheritance in Python. It ensures that the resolution order respects the consistency and priority of the inheritance hierarchy.\
\
14. MRO (Method Resolution Order) entries are resolved: This refers to the determination of the order in which base classes are searched for a particular attribute or method. The MRO defines the inheritance hierarchy and is calculated using the C3 linearization algorithm.\
\
15. Type annotations in Python refer to the practice of adding hints or metadata about the types of variables, function parameters, and return values in code. They are used to provide static type checking and improve code clarity, maintainability, and tooling support. They are optional and do not affect the runtime behavior of the code. Python remains a dynamically typed language, but type annotations allow developers to specify the expected types and enable type checking tools to catch potential errors.\
\
x: int = 5\
def add(a: int, b: int) -> int:\
return a + b\
\
16. The purpose of __class_getitem__() is to allow runtime parameterization of standard-library generic classes in order to more easily apply type hints to these classes.}
+85
View File
@@ -0,0 +1,85 @@
# Create a class that implements a context manager as described in 3.3.9.
# The context manager should keep track of an array of banana emojis.
# The class should be able to be used with with: so it adds a banana to the list, and upon exit, removes a banana from the list.
# class BananaManager:
# def __init__(self, bananas):
# self.bananas = bananas
# def __enter__(self):
# self.bananas.append("🍌")
# return self.bananas
# def __exit__(self, exc_type, exc_value, traceback):
# self.bananas.pop()
# # Usage of the banana manager
# banana_list = ["🍌", "🍌"]
# print("Before with statement:", banana_list)
# with BananaManager(banana_list) as adding_bananas:
# print("Inside with statement:", adding_bananas)
# print("After with statement:", banana_list)
# Questions: can you use a with block inside of another with block? (You might have to get creative for this to work.).
# If you can make that work, how many bananas are in the list?
# class BananaManager:
# def __init__(self, bananas):
# self.bananas = bananas
# def __enter__(self):
# self.bananas.append("🍌")
# return self.bananas
# def __exit__(self, exc_type, exc_value, traceback):
# self.bananas.pop()
# # Usage of the banana manager
# banana_list = ["🍌", "🍌"]
# print("Before with statement:", banana_list)
# with BananaManager(banana_list) as adding_bananas:
# print("Inside first with statement:", adding_bananas)
# with BananaManager(adding_bananas) as inside_bananas:
# print("Inside second with statement:", inside_bananas)
# print("After with statement:", banana_list)
# Another 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 BananaManager:
def __init__(self, bananas):
self.bananas = bananas
def __enter__(self):
self.bananas.append("🍌")
return self.bananas
def __exit__(self, exc_type, exc_value, traceback):
self.bananas.pop()
def outside_banana(self):
self.bananas.append("🍌")
# Usage of the banana manager
banana_list = ["🍌", "🍌"]
print("Before with statement:", banana_list)
bananamanager = BananaManager(banana_list)
bananamanager.outside_banana() # Add a banana outside of a with block
print("Outside adding a banana:", banana_list)
with BananaManager(banana_list) as adding_bananas:
print("Inside first with statement:", adding_bananas)
with BananaManager(adding_bananas) as inside_bananas:
print("Inside second with statement:", inside_bananas)
print("After with statement:", banana_list)
+89
View File
@@ -0,0 +1,89 @@
# import sys
# # Python 3
# print(sys.maxsize) # Output: 9223372036854775807 (on most platforms)
# list1 = [1, 2, 3, 4, 6]
# list2 = [2, 4, 6, 7, 8]
# # Find common values
# common_values = []
# for item in list1:
# if item in list2:
# common_values.append(item)
# # Find different values
# different_values = []
# for item in list1:
# if item not in list2:
# different_values.append(item)
# for item in list2:
# if item not in list1:
# different_values.append(item)
# print("Common values:", common_values) # Common values: [2, 4, 6]
# print("Different values:", different_values) # Different values: [1, 3, 7, 8]
# # Find common values
# common_values = set(list1) & set(list2)
# # Find different values
# different_values = set(list1) ^ set(list2)
# print("Common values:", common_values)
# print("Different values:", different_values)
# def outer_function():
# outer_variable = 10
# def inner_function():
# inner_variable = 20
# print("Inner function: ", inner_variable)
# print("Outer function (captured variable): ", outer_variable)
# return inner_function
# # Create a closure by calling the outer function
# closure = outer_function()
# # Access the cell object from the closure
# cell_object = closure.__closure__[0]
# # Access and print the value stored in the cell
# print("Cell contents: ", cell_object.cell_contents)
# # Set a new value to the cell
# cell_object.cell_contents = 30
# # Access and print the updated value from the closure
# print("Updated cell contents: ", closure.__closure__[0].cell_contents)
def greet(name):
message = f"Hello, {name}!"
print(message)
# Accessing code object
code_obj = greet.__code__
print(code_obj.co_name) # Output: greet
print(code_obj.co_argcount) # Output: 1 (number of arguments)
print(code_obj.co_varnames) # Output: ('name', 'message')
# Accessing function object
print(greet.__name__) # Output: greet
print(greet.__defaults__) # Output: None (no default arguments)
print(greet.__globals__) # Output: {'__name__': '__main__', ...} (global namespace)
# Modifying function object attributes
greet.__name__ = 'say_hello'
greet.__defaults__ = ('John',)
greet.__globals__['message'] = 'Welcome!'
# Call the modified function
greet() # Output: Hello, John!
+257
View File
@@ -0,0 +1,257 @@
scott@MacBook-Pro-6 Desktop % npm install -g @cloudflare/wrangler
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/lib/node_modules/@cloudflare
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/@cloudflare'
npm ERR! [Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/@cloudflare'] {
npm ERR! errno: -13,
npm ERR! code: 'EACCES',
npm ERR! syscall: 'mkdir',
npm ERR! path: '/usr/local/lib/node_modules/@cloudflare'
npm ERR! }
npm ERR!
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR!
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/scott/.npm/_logs/2023-06-19T09_56_53_005Z-debug-0.log
scott@MacBook-Pro-6 Desktop % sudo npm install -g @cloudflare/wrangler
Password:
npm WARN deprecated @cloudflare/wrangler@1.21.0: This package is for Wrangler v1.x and is no longer supported. Please visit https://www.npmjs.com/package/wrangler for Wrangler v2+.
added 22 packages in 3s
3 packages are looking for funding
run `npm fund` for details
scott@MacBook-Pro-6 Desktop % wrangler config
⛔ DEPRECATED ⛔
The version of Wrangler you are using is now deprecated.
Please update to the latest version of wrangler to prevent critical errors.
If originally installed via npm, run `npm uninstall -g @cloudflare/wrangler && npm install -g wrangler` to update to the latest version.
If originally installed via cargo, run `cargo uninstall wrangler && npm install -g wrangler`
Docs migration guide: https://developers.cloudflare.com/workers/wrangler/migration/migrating-from-wrangler-1/#update-wrangler-version
To find your API Token, go to https://dash.cloudflare.com/profile/api-tokens
and create it using the "Edit Cloudflare Workers" template.
Consider using `wrangler login` which only requires your Cloudflare username and password.
If you are trying to use your Global API Key instead of an API Token
(Not Recommended), run `wrangler config --api-key`.
Enter API Token:
bc70cf5fbbd43ab841faa22bb9b8ae67389a9
💁 Validating credentials...
Error: Authentication check failed. Please make sure your API token is correct.
⚠️ Code 6003: Invalid request headers
scott@MacBook-Pro-6 Desktop % curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer n27K9bdHXZJCUPq6jGU5F5N8okSFNQDsLvHP4u79" \
-H "Content-Type:application/json"
{"result":{"id":"1f5dd3d359342f37d9bfd5136697efd4","status":"active"},"success":true,"errors":[],"messages":[{"code":10000,"message":"This API Token is valid and active","type":null}]}% scott@MacBook-Pro-6 Desktop % wrangler config
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ ⛔ DEPRECATED ⛔ │
│ The version of Wrangler you are using is now deprecated. │
│ Please update to the latest version of wrangler to prevent critical errors. │
│ If originally installed via npm, run `npm uninstall -g @cloudflare/wrangler && npm install -g wrangler` to update to the latest version. │
│ If originally installed via cargo, run `cargo uninstall wrangler && npm install -g wrangler` │
│ Docs migration guide: https://developers.cloudflare.com/workers/wrangler/migration/migrating-from-wrangler-1/#update-wrangler-version │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ To find your API Token, go to https://dash.cloudflare.com/profile/api-tokens │
│ and create it using the "Edit Cloudflare Workers" template. │
│ │
│ Consider using `wrangler login` which only requires your Cloudflare username and password. │
│ │
│ If you are trying to use your Global API Key instead of an API Token │
│ (Not Recommended), run `wrangler config --api-key`. │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯
Enter API Token:
n27K9bdHXZJCUPq6jGU5F5N8okSFNQDsLvHP4u79
💁 Validating credentials...
Error: Permission denied (os error 13)
scott@MacBook-Pro-6 Desktop % wrangler config
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ ⛔ DEPRECATED ⛔ │
│ The version of Wrangler you are using is now deprecated. │
│ Please update to the latest version of wrangler to prevent critical errors. │
│ If originally installed via npm, run `npm uninstall -g @cloudflare/wrangler && npm install -g wrangler` to update to the latest version. │
│ If originally installed via cargo, run `cargo uninstall wrangler && npm install -g wrangler` │
│ Docs migration guide: https://developers.cloudflare.com/workers/wrangler/migration/migrating-from-wrangler-1/#update-wrangler-version │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ To find your API Token, go to https://dash.cloudflare.com/profile/api-tokens │
│ and create it using the "Edit Cloudflare Workers" template. │
│ │
│ Consider using `wrangler login` which only requires your Cloudflare username and password. │
│ │
│ If you are trying to use your Global API Key instead of an API Token │
│ (Not Recommended), run `wrangler config --api-key`. │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯
Enter API Token:
Enter API Token:
n27K9bdHXZJCUPq6jGU5F5N8okSFNQDsLvHP4u79
💁 Validating credentials...
Error: Permission denied (os error 13)💁 Validating credentials...
Error: Authentication check failed. Please make sure your API token is correct.
⚠️ Code 6003: Invalid request headers
scott@MacBook-Pro-6 Desktop % n27K9bdHXZJCUPq6jGU5F5N8okSFNQDsLvHP4u79
zsh: command not found: n27K9bdHXZJCUPq6jGU5F5N8okSFNQDsLvHP4u79
scott@MacBook-Pro-6 Desktop % 💁 Validating credentials...
zsh: command not found: 💁
scott@MacBook-Pro-6 Desktop % sudo wrangler config
Password:
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ ⛔ DEPRECATED ⛔ │
│ The version of Wrangler you are using is now deprecated. │
│ Please update to the latest version of wrangler to prevent critical errors. │
│ If originally installed via npm, run `npm uninstall -g @cloudflare/wrangler && npm install -g wrangler` to update to the latest version. │
│ If originally installed via cargo, run `cargo uninstall wrangler && npm install -g wrangler` │
│ Docs migration guide: https://developers.cloudflare.com/workers/wrangler/migration/migrating-from-wrangler-1/#update-wrangler-version │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ To find your API Token, go to https://dash.cloudflare.com/profile/api-tokens │
│ and create it using the "Edit Cloudflare Workers" template. │
│ │
│ Consider using `wrangler login` which only requires your Cloudflare username and password. │
│ │
│ If you are trying to use your Global API Key instead of an API Token │
│ (Not Recommended), run `wrangler config --api-key`. │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯
Enter API Token:
n27K9bdHXZJCUPq6jGU5F5N8okSFNQDsLvHP4u79
💁 Validating credentials...
✨ Successfully configured. You can find your configuration file at: /Users/scott/.wrangler/config/default.toml
╭────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ A new major version of wrangler is available! │
│ You can learn more about updating here: │
│ https://developers.cloudflare.com/workers/cli-wrangler/install-update#update │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────╯
scott@MacBook-Pro-6 Desktop % Users/scott/.wrangler/config/default.toml
zsh: no such file or directory: Users/scott/.wrangler/config/default.toml
scott@MacBook-Pro-6 Desktop % wrangler generate my-worker
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ ⛔ DEPRECATED ⛔ │
│ The version of Wrangler you are using is now deprecated. │
│ Please update to the latest version of wrangler to prevent critical errors. │
│ If originally installed via npm, run `npm uninstall -g @cloudflare/wrangler && npm install -g wrangler` to update to the latest version. │
│ If originally installed via cargo, run `cargo uninstall wrangler && npm install -g wrangler` │
│ Docs migration guide: https://developers.cloudflare.com/workers/wrangler/migration/migrating-from-wrangler-1/#update-wrangler-version │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
⬇️ Installing cargo-generate v0.5.0...
🔧 Creating project called `my-worker`...
✨ Done! New project created /Users/scott/Desktop/my-worker
╭────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ A new major version of wrangler is available! │
│ You can learn more about updating here: │
│ https://developers.cloudflare.com/workers/cli-wrangler/install-update#update │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────╯
scott@MacBook-Pro-6 Desktop % npm uninstall -g @cloudflare/wrangler && npm install -g wrangler
npm ERR! code EACCES
npm ERR! syscall rename
npm ERR! path /usr/local/lib/node_modules/@cloudflare/wrangler
npm ERR! dest /usr/local/lib/node_modules/@cloudflare/.wrangler-YTQXDq6U
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, rename '/usr/local/lib/node_modules/@cloudflare/wrangler' -> '/usr/local/lib/node_modules/@cloudflare/.wrangler-YTQXDq6U'
npm ERR! [Error: EACCES: permission denied, rename '/usr/local/lib/node_modules/@cloudflare/wrangler' -> '/usr/local/lib/node_modules/@cloudflare/.wrangler-YTQXDq6U'] {
npm ERR! errno: -13,
npm ERR! code: 'EACCES',
npm ERR! syscall: 'rename',
npm ERR! path: '/usr/local/lib/node_modules/@cloudflare/wrangler',
npm ERR! dest: '/usr/local/lib/node_modules/@cloudflare/.wrangler-YTQXDq6U'
npm ERR! }
npm ERR!
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR!
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/scott/.npm/_logs/2023-06-19T10_14_51_613Z-debug-0.log
scott@MacBook-Pro-6 Desktop % sudo npm uninstall -g @cloudflare/wrangler && npm install -g wrangler
Password:
removed 22 packages in 55ms
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /Users/scott/.npm/_cacache/content-v2/sha512/ba/97
npm ERR! errno EACCES
npm ERR!
npm ERR! Your cache folder contains root-owned files, due to a bug in
npm ERR! previous versions of npm which has since been addressed.
npm ERR!
npm ERR! To permanently fix this problem, please run:
npm ERR! sudo chown -R 501:20 "/Users/scott/.npm"
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/scott/.npm/_logs/2023-06-19T10_14_59_956Z-debug-0.log
scott@MacBook-Pro-6 Desktop % sudo chown -R 501:20 "/Users/scott/.npm"
scott@MacBook-Pro-6 Desktop % npm install -g wrangler
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/lib/node_modules/wrangler
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/wrangler'
npm ERR! [Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/wrangler'] {
npm ERR! errno: -13,
npm ERR! code: 'EACCES',
npm ERR! syscall: 'mkdir',
npm ERR! path: '/usr/local/lib/node_modules/wrangler'
npm ERR! }
npm ERR!
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR!
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/scott/.npm/_logs/2023-06-19T10_16_04_916Z-debug-0.log
scott@MacBook-Pro-6 Desktop % sudo npm install -g wrangler --unsafe-perm
npm WARN deprecated rollup-plugin-inject@3.0.2: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.
npm WARN deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead
added 107 packages in 25s
15 packages are looking for funding
run `npm fund` for details
scott@MacBook-Pro-6 Desktop % wrangler --version
⛅️ wrangler 3.1.0
------------------
+170
View File
@@ -0,0 +1,170 @@
overloading in python:
Function overloading refers to the ability to define multiple functions with the same name but different parameter lists.
However, in Python, you can achieve similar functionality by using default arguments and optional parameters. Here's an example:
def my_function(param1, param2=None):
if param2 is None:
# Perform action when only one parameter is provided
print("Parameter 1:", param1)
else:
# Perform action when both parameters are provided
print("Parameter 1:", param1)
print("Parameter 2:", param2)
In this example, the my_function function takes two parameters. The second parameter param2 is set to None by default, making it optional. Inside the function, you can check if param2 is None to determine whether it was provided or not.
superclass’s __new__() method:
The __new__() method in Python is a special method defined in a class that is responsible for creating and returning a new instance of that class. It is a static method that gets called before the __init__() method and is primarily used in the creation of immutable objects or when you need more control over the object creation process.
When dealing with inheritance, the __new__() method of the superclass is automatically called when creating an instance of a subclass. This happens before the subclass's __init__() method is invoked. The __new__() method of the superclass can be overridden in the subclass to customize the object creation process.
metaclasses:
Metaclasses in Python provide a way to define the behavior and structure of classes themselves. A metaclass is the class of a class, i.e., a class that defines how a class should be created, just as a class defines how an instance should be created. Metaclasses allow you to customize class creation and modify class attributes, methods, and behavior.
class constructor expression:
In Python, the class constructor is defined using the __init__() method. It is called automatically when an object is created from a class and is responsible for initializing the object's attributes.
class MyClass:
def __init__(self, param1, param2):
self.param1 = param1
self.param2 = param2
# Creating an instance of MyClass
obj = MyClass("Hello", 42)
gc module:
The gc module in Python provides an interface to the garbage collector and related functions for managing memory and collecting unused objects. It is part of Python's memory management system and allows you to control and monitor the garbage collection process.
“rich comparison” methods:
"Rich comparison" methods, also known as "comparison operators" or "comparison magic methods," are special methods in Python that allow objects to define their own behavior for comparison operations such as equality, inequality, less than, greater than, etc. These methods enable you to customize how objects are compared and ordered.
By implementing rich comparison methods in your classes, you can define custom rules for comparison operations and make objects behave in a way that suits your needs.
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def __eq__(self, other):
if isinstance(other, Rectangle):
return self.width == other.width and self.height == other.height
return False
def __lt__(self, other):
if isinstance(other, Rectangle):
return self.width * self.height < other.width * other.height
return NotImplemented
rect1 = Rectangle(3, 4)
rect2 = Rectangle(4, 5)
rect3 = Rectangle(3, 4)
print(rect1 == rect2) # Output: False
print(rect1 == rect3) # Output: True
print(rect1 < rect2) # Output: True
swapped-argument versions:
The "swapped-argument versions" of the rich comparison methods refer to the reversed version of the original comparison methods. These methods allow you to define the behavior when the operands of the comparison are swapped.
hashed collections:
Hashed collections in Python are data structures that use hashing algorithms to provide efficient lookup and retrieval of elements. They are designed to offer fast access to elements based on their unique hash values.
There are several hashed collection types available in Python's standard library:
dict (dictionary): A dictionary is an unordered collection of key-value pairs. It uses a hash table implementation, where the keys are hashed to provide fast lookup and retrieval of values. Dictionaries are commonly used when you need to map unique keys to corresponding values.
set: A set is an unordered collection of unique elements. It uses hash-based lookup to quickly determine membership of an element. Sets are useful when you want to store a collection of items without any duplicates and perform set operations such as union, intersection, and difference efficiently.
frozenset: Similar to a set, a frozenset is an immutable version of a set. Once created, its elements cannot be modified. Frozensets are often used as keys in dictionaries or elements in other sets because they are hashable.
salted in python:
In Python, salting is commonly used in password hashing and encryption algorithms. Here's an example of how salting can be implemented using the bcrypt library, which is a popular choice for secure password hashing:
import bcrypt
password = "my_password"
salt = bcrypt.gensalt()
# Hash the password with the generated salt
hashed_password = bcrypt.hashpw(password.encode(), salt)
# Verify the password
if bcrypt.checkpw(password.encode(), hashed_password):
print("Password is correct.")
else:
print("Password is incorrect.")
password hashing and encryption algorithms:
Password hashing and encryption algorithms are used to protect sensitive information like passwords by transforming them into a non-readable format. Here are some commonly used algorithms in Python:
bcrypt: bcrypt is a popular password hashing algorithm that incorporates a salt value and multiple iterations of hashing to increase the security of passwords. It is designed to be slow and computationally expensive, making it resistant to brute-force attacks. The bcrypt library in Python provides an easy-to-use interface for working with bcrypt.
Argon2: Argon2 is a state-of-the-art password hashing algorithm that won the Password Hashing Competition in 2015. It is designed to be memory-hard and resistant to various types of attacks, including GPU and ASIC attacks. The argon2 library in Python provides support for Argon2.
PBKDF2: PBKDF2 (Password-Based Key Derivation Function 2) is a key derivation function that applies a cryptographic hash function (such as SHA-1 or SHA-256) iteratively with a salt value to produce a derived key. It is widely used for password hashing. The hashlib library in Python provides support for various hash functions that can be used with PBKDF2.
scrypt: scrypt is a password-based key derivation function that is designed to be memory-hard and resistant to various types of attacks, including hardware-based attacks. It is commonly used for password storage. The scrypt library in Python provides support for scrypt.
AES: AES (Advanced Encryption Standard) is a symmetric encryption algorithm that is widely used for encrypting data. It is a block cipher with various key sizes (128-bit, 192-bit, or 256-bit) and encryption modes (such as ECB, CBC, or GCM). The cryptography library in Python provides support for AES encryption.
O(n2) complexity:
O(n^2) complexity, also known as quadratic complexity, refers to an algorithm whose execution time grows quadratically with the input size. It means that the time required to solve a problem double as the input size increases by a constant factor.
dict insertion:
Dictionary insertion in Python has an average time complexity of O(1) and a worst-case time complexity of O(n) in scenarios where collisions occur frequently.
intentional asymmetry:
Intentional asymmetry, in the context of cryptography, refers to the deliberate introduction of imbalance or non-uniformity in cryptographic algorithms or protocols. It is done to enhance security by making it difficult for attackers to exploit patterns or symmetries that could potentially weaken the system.
cryptography:
Cryptography is the practice of securing communication and data by transforming it into a form that is unintelligible to unauthorized individuals. It involves the use of cryptographic algorithms and protocols to ensure confidentiality, integrity, authentication, and non-repudiation of information.
runtime introspection of dynamic class attributes:
In Python, you can perform runtime introspection of dynamic class attributes using various built-in functions and attributes. Here are a few techniques:
dir() function: The dir() function returns a list of names in the current local or global scope. When called on a class or an instance, it returns a list of all attributes, including both static and dynamic attributes.
vars() function: The vars() function returns the __dict__ attribute of an object, which contains the object's attributes and their values as a dictionary. When called on a class, it returns the class's namespace dictionary.
getattr() function: The getattr() function allows you to dynamically access an attribute of an object by name. It takes two arguments: the object and the attribute name as a string. If the attribute exists, it is returned; otherwise, an optional default value can be provided.
Multiple inheritance:
Multiple inheritance is a feature in object-oriented programming languages that allows a class to inherit attributes and behaviors from multiple parent classes. In Python, you can create a class that inherits from multiple parent classes by listing them in the class definition separated by commas.
MRO:
MRO stands for Method Resolution Order and refers to the order in which methods are resolved or looked up in a class hierarchy that involves multiple inheritance. When a class inherits from multiple parent classes, the MRO determines the sequence in which the methods are searched for and executed.
lexical scoping:
Lexical scoping, also known as static scoping, is a scoping mechanism used in programming languages to determine the scope of variables and identifiers based on their location in the source code. In lexical scoping, the scope of a variable is determined by its position or context in the program's text or source code, rather than by runtime conditions.
divmod():
result = divmod(17, 5)
print(result) # Output: (3, 2)
pow():
result = pow(2, 3)
print(result) # Output: 8
^:
result = 5 ^ 3
print(result) # Output: 6
In this example, 5 ^ 3 performs a bitwise XOR operation on the numbers 5 and 3. The binary representation of 5 is 101, and the binary representation of 3 is 011. Performing the XOR operation on corresponding bits gives 110, which is equal to 6 in decimal.
&:
result = 12 & 7
print(result) # Output: 4
In this example, 12 & 7 performs a bitwise AND operation on the numbers 12 and 7. The binary representation of 12 is 1100, and the binary representation of 7 is 0111. Performing the AND operation on corresponding bits gives 0100, which is equal to 4 in decimal.
@:
In Python, the @ symbol is used as the matrix multiplication operator. It is also known as the "at" operator or "matmul" operator. The @ operator performs matrix multiplication between two arrays or matrices.
%:
result = 10 % 3
print(result) # Output: 1
<<:
result = 10 << 2
print(result) # Output: 40
In this example, 10 << 2 shifts the bits of the number 10 two positions to the left. The binary representation of 10 is 1010, and shifting it two positions to the left gives 101000, which is equal to 40 in decimal.
|:
result = 5 | 3
print(result) # Output: 7
In this example, 5 | 3 performs a bitwise OR operation on the numbers 5 and 3. The binary representation of 5 is 101, and the binary representation of 3 is 011. Performing the OR operation on corresponding bits gives 111, which is equal to 7 in decimal.
questions:
1.What is the purpose of using a constructor in a class?
2.How do you define a class method in Python?
3.What is the difference between an instance variable and a class variable?
4.How can you access an instance variable within a class method?
5.Can a class method modify the value of a class variable? Why or why not?
6.Explain the concept of method overloading with an example.
7.What is the difference between a shallow copy and a deep copy of an object?
8.How do you define a static method in Python? What is its significance?
9."What is the difference between mutable sequences and immutable sequences in programming, and when would you choose to use one over the other?"
10.Consider a scenario where you have a parent class called Animal and two child classes called Cat and Dog. If you have a variable named animal which is of type Animal, how would you determine if it is an instance of the Cat class using logical deduction instead of direct comparison?
+165
View File
@@ -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.