Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions contrib/advanced-python/descriptors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
### Descriptors in Python

Descriptors are a powerful feature in Python that allow you to customize the behavior of attribute access. They enable you to define how an attribute is accessed, set, and deleted. Descriptors are used to manage the attributes of different classes using methods in a single class.

### What is a Descriptor?

A descriptor is any object that implements at least one of the following methods: `__get__()`, `__set__()`, and `__delete__()`. These methods are used to define the behavior of attribute access.

- `__get__(self, instance, owner)`: Defines behavior for accessing the attribute.
- `__set__(self, instance, value)`: Defines behavior for setting the attribute.
- `__delete__(self, instance)`: Defines behavior for deleting the attribute.

### Creating a Descriptor

Let's create a basic descriptor that manages a simple attribute.

```python
class Descriptor:
def __init__(self, name):
self.name = name

def __get__(self, instance, owner):
print(f"Getting: {self.name}")
return instance.__dict__[self.name]

def __set__(self, instance, value):
print(f"Setting: {self.name} to {value}")
instance.__dict__[self.name] = value

def __delete__(self, instance):
print(f"Deleting: {self.name}")
del instance.__dict__[self.name]

class MyClass:
attribute = Descriptor('attribute')

obj = MyClass()
obj.attribute = 42 # Setting: attribute to 42
print(obj.attribute) # Getting: attribute \n 42
del obj.attribute # Deleting: attribute
```

### How Descriptors Work

When you access an attribute on an instance of a class, Python will check if the attribute is a descriptor by looking for __get__, __set__, or __delete__ methods. If any of these methods are present, the corresponding descriptor method is called.

### Types of Descriptors

- **Data Descriptors**: Implement both `__get__` and `__set__` (or `__delete__`). They have priority over instance dictionaries.

- **Non-Data Descriptors**: Implement only `__get__`. They are used for methods and attributes where setting is not required.

### Data Descriptor Example

```python
class DataDescriptor:
def __init__(self, name):
self.name = name

def __get__(self, instance, owner):
return instance.__dict__.get(self.name, None)

def __set__(self, instance, value):
instance.__dict__[self.name] = value

class MyClass:
attribute = DataDescriptor('attribute')

obj = MyClass()
obj.attribute = 10
print(obj.attribute) # Output: 10
```
### Non-Data Descriptor Example
```python
class NonDataDescriptor:
def __get__(self, instance, owner):
return "Non-Data Descriptor"

class MyClass:
attribute = NonDataDescriptor()

obj = MyClass()
print(obj.attribute) # Output: Non-Data Descriptor
obj.attribute = 10
print(obj.attribute) # Output: 10 (Overridden by instance dictionary)
```

### Practical Uses of Descriptors

- **Type of Checking and Validation:**

Type checking and validation ensure that your program's data is of the correct type and meets specific rules. This prevents bugs and makes your code more reliable.

```python
class Integer:
def __init__(self, name):
self.name = name

def __get__(self, instance, owner):
return instance.__dict__[self.name]

def __set__(self, instance, value):
if not isinstance(value, int):
raise ValueError(f"{self.name} must be an integer")
instance.__dict__[self.name] = value

class Point:
x = Integer('x')
y = Integer('y')

p = Point()
p.x = 5
p.y = 'a' # Raises ValueError: y must be an integer
```

- **Lazy Attributes:**

These are attributes that are only calculated when they are first needed. This saves resources by delaying the computation until necessary.

```python
class LazyProperty:
def __init__(self, func):
self.func = func
self.name = func.__name__

def __get__(self, instance, owner):
if instance is None:
return self
value = self.func(instance)
setattr(instance, self.name, value)
return value

class MyClass:
@LazyProperty
def expensive_operation(self):
print("Computing...")
return 42

obj = MyClass()
print(obj.expensive_operation) # Computing... \n 42
print(obj.expensive_operation) # 42 (No recomputation)
```
- **Computed Properties:**

These are properties that are calculated each time they are accessed, based on other data in the object. They ensure the value is always current.
```python
class Celsius:
def __init__(self, temperature=0):
self._temperature = temperature

def get_temperature(self):
return self._temperature

def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value

temperature = property(get_temperature, set_temperature)

c = Celsius()
c.temperature = 37
print(c.temperature) # Output: 37
```
1 change: 1 addition & 0 deletions contrib/advanced-python/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
- [Magic Methods](magic-methods.md)
- [Asynchronous Context Managers & Generators](asynchronous-context-managers-generators.md)
- [Threading](threading.md)
- [Descriptors](descriptors.md)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions contrib/ds-algorithms/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
- [Dijkstra's Algorithm](dijkstra.md)
- [Deque](deque.md)
- [Tree Traversals](tree-traversal.md)
- [Kadane's Algorithm](kadanes-algorithm.md)
69 changes: 69 additions & 0 deletions contrib/ds-algorithms/kadanes-algorithm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
### Kadane's Algorithm

### Theory

**Kadane's Algorithm** is used to find the maximum sum of a contiguous subarray within a one-dimensional numerical array of integers. It efficiently handles both positive and negative numbers and is known for its linear time complexity.

### Algorithm

1. **Initialization**

- Initialize two variables: **max_current** to store the maximum sum of the subarray ending at the current position, and **max_global** to store the maximum sum found so far across all subarrays

2. **Iteration:**

- For each element in the array:

- Update **max_current** to be the maximum of the current element itself or the sum of **max_current** and the current element (this decides whether to start a new subarray or to continue the existing one).

- Update **max_global** to be the maximum of **max_global** and **max_current**.

3. **Result:**

- After iterating through the array, **max_global** will hold the maximum sum of any contiguous subarray.

### Time Complexity

**O(n)**: The algorithm runs in linear time because it makes a single pass through the array.

### Space Complexity

**O(1)**: The algorithm uses only a constant amount of extra space.

### Code

```Python
def kadane(arr):
max_current = arr[0]
max_global = arr[0]

for i in range(1, len(arr)):
max_current = max(arr[i], max_current + arr[i])
if max_current > max_global:
max_global = max_current

return max_global

# Example usage:
arr = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
print("Maximum sum of contiguous subarray:", kadane(arr)) # Output: 6
```
### Example Explanation

Given the array [-2, 1, -3, 4, -1, 2, 1, -5, 4], **Kadane's Algorithm** will find that the subarray [4, -1, 2, 1] has the maximum sum of 6.

### Computational Analysis

- **Time Complexity:** O(n), where n is the number of elements in the array.

- **Space Complexity:** O(1), since the algorithm uses a constant amount of extra space regardless of the input size.

### Applications

- **Financial Data Analysis:** Finding maximum profit from daily stock prices.

- **Image Processing:** Detecting regions with maximum brightness in images.

- **Genomics:** Analyzing DNA sequences for regions with the highest similarity.

**Kadane's Algorithm** is widely used due to its simplicity and efficiency in solving the maximum subarray sum problem.