Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate version specific scripts p2 #288

Merged
merged 3 commits into from
Mar 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ __Structural Patterns__:
| [composite](patterns/structural/composite.py) | lets clients treat individual objects and compositions uniformly |
| [decorator](patterns/structural/decorator.py) | wrap functionality with other functionality in order to affect outputs |
| [facade](patterns/structural/facade.py) | use one class as an API to a number of others |
| [flyweight](patterns/structural/flyweight.py) | transparently reuse existing instances of objects with similar/identical state |
| [flyweight](patterns/structural/flyweight__py3.py) | transparently reuse existing instances of objects with similar/identical state |
| [front_controller](patterns/structural/front_controller.py) | single handler requests coming to the application |
| [mvc](patterns/structural/mvc.py) | model<->view<->controller (non-strict relationships) |
| [proxy](patterns/structural/proxy.py) | an object funnels operations to something else |
Expand Down Expand Up @@ -69,7 +69,7 @@ __Others__:

| Pattern | Description |
|:-------:| ----------- |
| [blackboard](patterns/other/blackboard.py) | architectural model, assemble different sub-system knowledge to build a solution, AI approach - non gang of four pattern |
| [blackboard](patterns/other/blackboard__py3.py) | architectural model, assemble different sub-system knowledge to build a solution, AI approach - non gang of four pattern |
| [graph_search](patterns/other/graph_search.py) | graphing algorithms - non gang of four pattern |
| [hsm](patterns/other/hsm/hsm.py) | hierarchical state machine - non gang of four pattern |

Expand Down
File renamed without changes.
122 changes: 122 additions & 0 deletions patterns/other/blackboard__py3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
@author: Eugene Duboviy <[email protected]> | github.com/duboviy

In Blackboard pattern several specialised sub-systems (knowledge sources)
assemble their knowledge to build a possibly partial or approximate solution.
In this way, the sub-systems work together to solve the problem,
where the solution is the sum of its parts.

https://en.wikipedia.org/wiki/Blackboard_system
"""

import abc
import random


class Blackboard(object):
def __init__(self):
self.experts = []
self.common_state = {
'problems': 0,
'suggestions': 0,
'contributions': [],
'progress': 0, # percentage, if 100 -> task is finished
}

def add_expert(self, expert):
self.experts.append(expert)


class Controller(object):
def __init__(self, blackboard):
self.blackboard = blackboard

def run_loop(self):
while self.blackboard.common_state['progress'] < 100:
for expert in self.blackboard.experts:
if expert.is_eager_to_contribute:
expert.contribute()
return self.blackboard.common_state['contributions']


class AbstractExpert(metaclass=abc.ABCMeta):

def __init__(self, blackboard):
self.blackboard = blackboard

@property
@abc.abstractmethod
def is_eager_to_contribute(self):
raise NotImplementedError('Must provide implementation in subclass.')

@abc.abstractmethod
def contribute(self):
raise NotImplementedError('Must provide implementation in subclass.')


class Student(AbstractExpert):
@property
def is_eager_to_contribute(self):
return True

def contribute(self):
self.blackboard.common_state['problems'] += random.randint(1, 10)
self.blackboard.common_state['suggestions'] += random.randint(1, 10)
self.blackboard.common_state['contributions'] += [self.__class__.__name__]
self.blackboard.common_state['progress'] += random.randint(1, 2)


class Scientist(AbstractExpert):
@property
def is_eager_to_contribute(self):
return random.randint(0, 1)

def contribute(self):
self.blackboard.common_state['problems'] += random.randint(10, 20)
self.blackboard.common_state['suggestions'] += random.randint(10, 20)
self.blackboard.common_state['contributions'] += [self.__class__.__name__]
self.blackboard.common_state['progress'] += random.randint(10, 30)


class Professor(AbstractExpert):
@property
def is_eager_to_contribute(self):
return True if self.blackboard.common_state['problems'] > 100 else False

def contribute(self):
self.blackboard.common_state['problems'] += random.randint(1, 2)
self.blackboard.common_state['suggestions'] += random.randint(10, 20)
self.blackboard.common_state['contributions'] += [self.__class__.__name__]
self.blackboard.common_state['progress'] += random.randint(10, 100)


if __name__ == '__main__':
blackboard = Blackboard()

blackboard.add_expert(Student(blackboard))
blackboard.add_expert(Scientist(blackboard))
blackboard.add_expert(Professor(blackboard))

c = Controller(blackboard)
contributions = c.run_loop()

from pprint import pprint

pprint(contributions)

### OUTPUT ###
# ['Student',
# 'Student',
# 'Scientist',
# 'Student',
# 'Scientist',
# 'Student',
# 'Scientist',
# 'Student',
# 'Scientist',
# 'Student',
# 'Scientist',
# 'Professor']
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,9 @@ def __repr__(self):
return "<Card: %s%s>" % (self.value, self.suit)


def with_metaclass(meta, *bases):
""" Provide python cross-version metaclass compatibility. """
return meta("NewBase", bases, {})
class Card2(object):
__metaclass__ = FlyweightMeta


class Card2(with_metaclass(FlyweightMeta)):
def __init__(self, *args, **kwargs):
# print('Init {}: {}'.format(self.__class__, (args, kwargs)))
pass
Expand Down
135 changes: 135 additions & 0 deletions patterns/structural/flyweight__py3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
*What is this pattern about?
This pattern aims to minimise the number of objects that are needed by
a program at run-time. A Flyweight is an object shared by multiple
contexts, and is indistinguishable from an object that is not shared.

The state of a Flyweight should not be affected by it's context, this
is known as its intrinsic state. The decoupling of the objects state
from the object's context, allows the Flyweight to be shared.

*What does this example do?
The example below sets-up an 'object pool' which stores initialised
objects. When a 'Card' is created it first checks to see if it already
exists instead of creating a new one. This aims to reduce the number of
objects initialised by the program.

*References:
http://codesnipers.com/?q=python-flyweights

*TL;DR80
Minimizes memory usage by sharing data with other similar objects.
"""

import weakref


class FlyweightMeta(type):
def __new__(mcs, name, parents, dct):
"""
Set up object pool

:param name: class name
:param parents: class parents
:param dct: dict: includes class attributes, class methods,
static methods, etc
:return: new class
"""
dct['pool'] = weakref.WeakValueDictionary()
return super(FlyweightMeta, mcs).__new__(mcs, name, parents, dct)

@staticmethod
def _serialize_params(cls, *args, **kwargs):
"""
Serialize input parameters to a key.
Simple implementation is just to serialize it as a string
"""
args_list = list(map(str, args))
args_list.extend([str(kwargs), cls.__name__])
key = ''.join(args_list)
return key

def __call__(cls, *args, **kwargs):
key = FlyweightMeta._serialize_params(cls, *args, **kwargs)
pool = getattr(cls, 'pool', {})

instance = pool.get(key)
if instance is None:
instance = super(FlyweightMeta, cls).__call__(*args, **kwargs)
pool[key] = instance
return instance


class Card(object):

"""The object pool. Has builtin reference counting"""

_CardPool = weakref.WeakValueDictionary()

"""Flyweight implementation. If the object exists in the
pool just return it (instead of creating a new one)"""

def __new__(cls, value, suit):
obj = Card._CardPool.get(value + suit)
if not obj:
obj = object.__new__(cls)
Card._CardPool[value + suit] = obj
obj.value, obj.suit = value, suit
return obj

# def __init__(self, value, suit):
# self.value, self.suit = value, suit

def __repr__(self):
return "<Card: %s%s>" % (self.value, self.suit)


class Card2(metaclass=FlyweightMeta):
def __init__(self, *args, **kwargs):
# print('Init {}: {}'.format(self.__class__, (args, kwargs)))
pass


if __name__ == '__main__':
# comment __new__ and uncomment __init__ to see the difference
c1 = Card('9', 'h')
c2 = Card('9', 'h')
print(c1, c2)
print(c1 == c2, c1 is c2)
print(id(c1), id(c2))

c1.temp = None
c3 = Card('9', 'h')
print(hasattr(c3, 'temp'))
c1 = c2 = c3 = None
c3 = Card('9', 'h')
print(hasattr(c3, 'temp'))

# Tests with metaclass
instances_pool = getattr(Card2, 'pool')
cm1 = Card2('10', 'h', a=1)
cm2 = Card2('10', 'h', a=1)
cm3 = Card2('10', 'h', a=2)

assert (cm1 == cm2) and (cm1 != cm3)
assert (cm1 is cm2) and (cm1 is not cm3)
assert len(instances_pool) == 2

del cm1
assert len(instances_pool) == 2

del cm2
assert len(instances_pool) == 1

del cm3
assert len(instances_pool) == 0

### OUTPUT ###
# (<Card: 9h>, <Card: 9h>)
# (True, True)
# (31903856, 31903856)
# True
# False
37 changes: 0 additions & 37 deletions tests/structural/test_flyweight.py

This file was deleted.