Skip to content

Commit

Permalink
new notes
Browse files Browse the repository at this point in the history
  • Loading branch information
FelipeMarcelino committed Jul 20, 2024
1 parent 4f94d6d commit 38f743c
Show file tree
Hide file tree
Showing 12 changed files with 329 additions and 66 deletions.
80 changes: 80 additions & 0 deletions 2ot8-python-strategy-with-functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Python Strategy with Functions
#designpattern #datastructure #class #apply #python #programming

- A more concise code compared to the #[[python-strategy]] using [[a8bt-high-order-functions]]

```python
from collections.abc import Sequence
from dataclasses import dataclass
from decimal import Decimal
from typing import Optional, Callable, NamedTuple
class Customer(NamedTuple):
name: str
fidelity: int

class LineItem(NamedTuple):
product: str
quantity: int
price: Decimal
def total(self):
return self.price * self.quantity

@dataclass(frozen=True)
class Order: # the Context
customer: Customer
cart: Sequence[LineItem]
promotion: Optional[Callable[['Order'], Decimal]] = None
def total(self) -> Decimal:
totals = (item.total() for item in self.cart)
return sum(totals, start=Decimal(0))
def due(self) -> Decimal:
if self.promotion is None:
discount = Decimal(0)
else:
discount = self.promotion(self)
return self.total() - discount
def __repr__(self):
return f'<Order total: {self.total():.2f} due: {self.due():.2f}>'

def fidelity_promo(order: Order) -> Decimal:
"""5% discount for customers with 1000 or more fidelity points"""
if order.customer.fidelity >= 1000:
return order.total() * Decimal('0.05')
return Decimal(0)

def bulk_item_promo(order: Order) -> Decimal:
"""10% discount for each LineItem with 20 or more units"""
discount = Decimal(0)
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * Decimal('0.1')
return discount

def large_order_promo(order: Order) -> Decimal:
"""7% discount for orders with 10 or more distinct items"""
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * Decimal('0.07')
return Decimal(0)

>>> joe = Customer('John Doe', 0)
>>> ann = Customer('Ann Smith', 1100)
>>> cart = [LineItem('banana', 4, Decimal('.5')),
... LineItem('apple', 10, Decimal('1.5')),
... LineItem('watermelon', 5, Decimal(5))]
>>> Order(joe, cart, fidelity_promo)
<Order total: 42.00 due: 42.00>
>>> Order(ann, cart, fidelity_promo)
<Order total: 42.00 due: 39.90>
>>> banana_cart = [LineItem('banana', 30, Decimal('.5')),
... LineItem('apple', 10, Decimal('1.5'))]
>>> Order(joe, banana_cart, bulk_item_promo)
<Order total: 30.00 due: 28.50>
>>> long_cart = [LineItem(str(item_code), 1, Decimal(1))
... for item_code in range(10)]
>>> Order(joe, long_cart, large_order_promo)
<Order total: 10.00 due: 9.30>
>>> Order(joe, cart, large_order_promo)
<Order total: 42.00 due: 42.00>

```
2 changes: 1 addition & 1 deletion aswx-python-decorator-implementation-with-parameters.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Python Decorator Implementation with Parameters
# Python Decorator Clock Implementation with Parameters
#designpattern #python #decorator

```python
Expand Down
65 changes: 4 additions & 61 deletions cea0-decorators.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,13 @@
# Python Decorators
#python #designpattern #programming #decorator
#python #designpattern #programming #decorator #index

- Is a type of #[[dcdy-callable-objects]]
- The objective: Decorate another function and take this function as argument
- Most part of the decorators change the function decorated
- Decorators are defined in one module and used and other modules as well
- They run on the step of `import time`

```python
@decorate
def target():
print("running target")
```
- Code above transform into: `target = decorate(target)`

```python
#file registration.py
registry = []
def register(func):
print(f'running register({func})')
registry.append(func)
return func
@register
def f1():
print('running f1()')
@register
def f2():
print('running f2()')
def f3():
print('running f3()')
def main():
print('running main()')
print('registry ->', registry)
f1()
f2()
f3()
if __name__ == '__main__':
main()

$python registration.py
running register(<function f1 at 0x100631bf8>)
running register(<function f2 at 0x100631c80>)
running main()
registry -> [<function f1 at 0x100631bf8>, <function f2 at 0x100631c80>]
running f1()
running f2()
running f3()
```

```python
>>> import registration
running register(<function f1 at 0x10063b1e0>)
running register(<function f2 at 0x10063b268>)
>>> registration.registry
[<function f1 at 0x10063b1e0>, <function f2 at 0x10063b268>]
```
- [[z2az-python-decorator-concept]]#
- [[x6ii-register-decorator]]#
- [[wmjn-python-decorator-implementation]]#
- [[aswx-python-decorator-implementation-with-parameters]]#
- [[naei-cache-lru-cache-singledispatch]]#
- [[hl16-parameterized-decorators]]#

> [!tip] Quote
> * A decorator is a function or another callable<br>
> * A decorator may replace decorated function with a different one<br>
> * Decorators are executed immediately when a module is loaded<br>
> Ramlho, 2022, p306
- [[o8vo-decorators-strategy]]#

### References
- Ramalho, 2022, p302-307
2 changes: 1 addition & 1 deletion hl16-parameterized-decorators.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Parameterized Decorators
#python #decorator #designpattern #programming
#python #decorator #designpattern #programming #apply

- To create a decorator with parameters is necessary to build a **decorator factory**
- This decorator factory will accept parameter and pass to the decorator itself
Expand Down
3 changes: 1 addition & 2 deletions naei-cache-lru-cache-singledispatch.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# cache, lru-cache, singledispatch
#python #efficiency #decorator #designpattern #programming
#python #efficiency #decorator #designpattern #programming #overriding

## Cache

Expand Down Expand Up @@ -40,5 +40,4 @@ if __name__ == '__main__':
- [[rugi-singledispatch-implementation]]#

### Reference

- Ramalho, 2022, p320-329
44 changes: 44 additions & 0 deletions o8vo-decorators-strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Python Decorators + Strategy
#designpattern #python #programming #apply #datastructure #combination


- The decorator is used to register each promo type into a list, so in that way we can select the best promo to apply
for each person

```python
Promotion = Callable[[Order], Decimal]

promos: list[Promotion] = []

def promotion(promo: Promotion) -> Promotion:
promos.append(promo)
return promo

def best_promo(order: Order) -> Decimal:
"""Compute the best discount available"""
return max(promo(order) for promo in promos)

@promotion
def fidelity(order: Order) -> Decimal:
"""5% discount for customers with 1000 or more fidelity points"""
if order.customer.fidelity >= 1000:
return order.total() * Decimal('0.05')
return Decimal(0)

@promotion
def bulk_item(order: Order) -> Decimal:
"""10% discount for each LineItem with 20 or more units"""
discount = Decimal(0)
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * Decimal('0.1')
return discount

@promotion
def large_order(order: Order) -> Decimal:
"""7% discount for orders with 10 or more distinct items"""
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * Decimal('0.07')
return Decimal(0)
```
119 changes: 119 additions & 0 deletions otjt-python-strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Python Strategy
#designpattern #datastructure #programming #python #class #apply



```mermaid
---
title: Strategy Promotion Example (Ramalho, 2022, p343)
---
classDiagram
Promotion <|-- LargeOrderPromo
Promotion <|-- FidelityPromo
Promotion <|-- BulkItemPromo
Promotion <--o Order: Aggregation
Promotion: discount()
class Order {
total()
due()
}
class LargeOrderPromo{
discount()
}
class FidelityPromo{
discount()
}
class BulkItemPromo{
discount()
}
```

```python
from abc import ABC, abstractmethod
from collections.abc import Sequence
from decimal import Decimal
from typing import NamedTuple, Optional

class Customer(NamedTuple):
name: str
fidelity: int

class LineItem(NamedTuple):
product: str
quantity: int
price: Decimal
def total(self) -> Decimal:
return self.price * self.quantity

class Order(NamedTuple): # the Context
customer: Customer
cart: Sequence[LineItem]
promotion: Optional['Promotion'] = None
def total(self) -> Decimal:
totals = (item.total() for item in self.cart)
return sum(totals, start=Decimal(0))
def due(self) -> Decimal:
if self.promotion is None:
discount = Decimal(0)
else:
discount = self.promotion.discount(self)
return self.total() - discount
def __repr__(self):
return f'<Order total: {self.total():.2f} due: {self.due():.2f}>'

class Promotion(ABC): # the Strategy: an abstract base class
@abstractmethod
def discount(self, order: Order) -> Decimal:
"""Return discount as a positive dollar amount"""

class FidelityPromo(Promotion): # first Concrete Strategy
"""5% discount for customers with 1000 or more fidelity points"""
def discount(self, order: Order) -> Decimal:
rate = Decimal('0.05')
if order.customer.fidelity >= 1000:
return order.total() * rate
return Decimal(0)

class BulkItemPromo(Promotion): # second Concrete Strategy
"""10% discount for each LineItem with 20 or more units"""
def discount(self, order: Order) -> Decimal:
discount = Decimal(0)
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * Decimal('0.1')
return discount

class LargeOrderPromo(Promotion): # third Concrete Strategy
"""7% discount for orders with 10 or more distinct items"""
def discount(self, order: Order) -> Decimal:
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * Decimal('0.07')
return Decimal(0)

>>> joe = Customer('John Doe', 0)
>>> ann = Customer('Ann Smith', 1100)
>>> cart = (LineItem('banana', 4, Decimal('.5')),
... LineItem('apple', 10, Decimal('1.5')),
... LineItem('watermelon', 5, Decimal(5)))
>>> Order(joe, cart, FidelityPromo())
<Order total: 42.00 due: 42.00>
>>> Order(ann, cart, FidelityPromo())
<Order total: 42.00 due: 39.90>
>>> banana_cart = (LineItem('banana', 30, Decimal('.5')),
... LineItem('apple', 10, Decimal('1.5')))
>>> Order(joe, banana_cart, BulkItemPromo())
<Order total: 30.00 due: 28.50>
>>> long_cart = tuple(LineItem(str(sku), 1, Decimal(1))
... for sku in range(10))
>>> Order(joe, long_cart, LargeOrderPromo())
<Order total: 10.00 due: 9.30>
>>> Order(joe, cart, LargeOrderPromo())
<Order total: 42.00 due: 42.00>
```
- As you can see the strategy concert class above only have one function. Given that, we can transform it to plain
functions instead of use classes. We can take benefit of high order functions ([[a8bt-high-order-functions]]) and implement it in a more concise way.
- [[2ot8-python-strategy-with-functions]]# and implement it in a more concise way and implement it in a more concise way.
- Usually `strategy` deal with data inside a context and does have internal state (instance attribute)
- In the code above the context is the class `Order`
- [[o8vo-decorators-strategy]]#
1 change: 1 addition & 0 deletions tds4-python.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
- [[dcdy-callable-objects]]#
- [[135k-keyword-only-arguments]]#
- [[7hky-python-variable-scope]]#
- [[zewz-design-patterns]]#
2 changes: 1 addition & 1 deletion wmjn-python-decorator-implementation.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Python Decorator Implementation
# Python Decorator Clock Implementation
#decorator #designpattern #apply #python #programming

```python
Expand Down
Loading

0 comments on commit 38f743c

Please sign in to comment.