8000 Merge pull request #288 from gyermolenko/separate_version_specific_sc… · faif/python-patterns@6726564 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6726564

Browse files
authored
Merge pull request #288 from gyermolenko/separate_version_specific_scripts_p2
Separate version specific scripts p2
2 parents ba45647 + c1ab03c commit 6726564

File tree

6 files changed

+261
-44
lines changed

6 files changed

+261
-44
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ __Structural Patterns__:
2828
| [composite](patterns/structural/composite.py) | lets clients treat individual objects and compositions uniformly |
2929
| [decorator](patterns/structural/decorator.py) | wrap functionality with other functionality in order to affect outputs |
3030
| [facade](patterns/structural/facade.py) | use one class as an API to a number of others |
31-
| [flyweight](patterns/structural/flyweight.py) | transparently reuse existing instances of objects with similar/identical state |
31+
| [flyweight](patterns/structural/flyweight__py3.py) | transparently reuse existing instances of objects with similar/identical state |
3232
| [front_controller](patterns/structural/front_controller.py) | single handler requests coming to the application |
3333
| [mvc](patterns/structural/mvc.py) | model<->view<->controller (non-strict relationships) |
3434
| [proxy](patterns/structural/proxy.py) | an object funnels operations to something else |
@@ -69,7 +69,7 @@ __Others__:
6969

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

File renamed without changes.

patterns/other/blackboard__py3.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
@author: Eugene Duboviy <eugene.dubovoy@gmail.com> | github.com/duboviy
6+
7+
In Blackboard pattern several specialised sub-systems (knowledge sources)
8+
assemble their knowledge to build a possibly partial or approximate solution.
9+
In this way, the sub-systems work together to solve the problem,
10+
where the solution is the sum of its parts.
11+
12+
https://en.wikipedia.org/wiki/Blackboard_system
13+
"""
14+
15+
import abc
16+
import random
17+
18+
19+
class Blackboard(object):
20+
def __init__(self):
21+
self.experts = []
22+
self.common_state = {
23+
'problems': 0,
24+
'suggestions': 0,
25+
'contributions': [],
26+
'progress': 0, # percentage, if 100 -> task is finished
27+
}
28+
29+
def add_expert(self, expert):
30+
self.experts.append(expert)
31+
32+
33+
class Controller(object):
34+
def __init__(self, blackboard):
35+
self.blackboard = blackboard
36+
37+
def run_loop(self):
38+
while self.blackboard.common_state['progress'] < 100:
39+
for expert in self.blackboard.experts:
40+
if expert.is_eager_to_contribute:
41+
expert.contribute()
42+
return self.blackboard.common_state['contributions']
43+
44+
45+
class AbstractExpert(metaclass=abc.ABCMeta):
46+
47+
def __init__(self, blackboard):
48+
self.blackboard = blackboard
49+
50+
@property
51+
@abc.abstractmethod
52+
def is_eager_to_contribute(self):
53+
raise NotImplementedError('Must provide implementation in subclass.')
54+
55+
@abc.abstractmethod
56+
def contribute(self):
57+
raise NotImplementedError('Must provide implementation in subclass.')
58+
59+
60+
class Student(AbstractExpert):
61+
@property
62+
def is_eager_to_contribute(self):
63+
return True
64+
65+
def contribute(self):
66+
self.blackboard.common_state['problems'] += random.randint(1, 10)
67+
self.blackboard.common_state['suggestions'] += random.randint(1, 10)
68+
self.blackboard.common_state['contributions'] += [self.__class__.__name__]
69+
self.blackboard.common_state['progress'] += random.randint(1, 2)
70+
71+
72+
class Scientist(AbstractExpert):
73+
@property
74+
def is_eager_to_contribute(self):
75+
return random.randint(0, 1)
76+
77+
def contribute(self):
78+
self.blackboard.common_state['problems'] += random.randint(10, 20)
79+
self.blackboard.common_state['suggestions'] += random.randint(10, 20)
80+
self.blackboard.common_state['contributions'] += [self.__class__.__name__]
81+
self.blackboard.common_state['progress'] += random.randint(10, 30)
82+
83+
84+
class Professor(AbstractExpert):
85+
@property
86+
def is_eager_to_contribute(self):
87+
return True if self.blackboard.common_state['problems'] > 100 else False
88+
89+
def contribute(self):
90+
self.blackboard.common_state['problems'] += random.randint(1, 2)
91+
self.blackboard.common_state['suggestions'] += random.randint(10, 20)
92+
self.blackboard.common_state['contributions'] += [self.__class__.__name__]
93+
self.blackboard.common_state['progress'] += random.randint(10, 100)
94+
95+
96+
if __name__ == '__main__':
97+
blackboard = Blackboard()
98+
99+
blackboard.add_expert(Student(blackboard))
100+
blackboard.add_expert(Scientist(blackboard))
101+
blackboard.add_expert(Professor(blackboard))
102+
103+
c = Controller(blackboard)
104+
contributions = c.run_loop()
105+
106+
from pprint import pprint
107+
108+
pprint(contributions)
109+
110+
### OUTPUT ###
111+
# ['Student',
112+
# 'Student',
113+
# 'Scientist',
114+
# 'Student',
115+
# 'Scientist',
116+
# 'Student',
117+
# 'Scientist',
118+
# 'Student',
119+
# 'Scientist',
120+
# 'Student',
121+
# 'Scientist',
122+
# 'Professor']

patterns/structural/flyweight.py renamed to patterns/structural/flyweight__py2.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,9 @@ def __repr__(self):
8787
return "<Card: %s%s>" % (self.value, self.suit)
8888

8989

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

94-
95-
class Card2(with_metaclass(FlyweightMeta)):
9693
def __init__(self, *args, **kwargs):
9794
# print('Init {}: {}'.format(self.__class__, (args, kwargs)))
9895
pass

patterns/structural/flyweight__py3.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
*What is this pattern about?
6+
This pattern aims to minimise the number of objects that are needed by
7+
a program at run-time. A Flyweight is an object shared by multiple
8+
contexts, and is indistinguishable from an object that is not shared.
9+
10+
The state of a Flyweight should not be affected by it's context, this
11+
is known as its intrinsic state. The decoupling of the objects state
12+
from the object's context, allows the Flyweight to be shared.
13+
14+
*What does this example do?
15+
The example below sets-up an 'object pool' which stores initialised
16+
objects. When a 'Card' is created it first checks to see if it already
17+
exists instead of creating a new one. This aims to reduce the number of
18+
objects initialised by the program.
19+
20+
*References:
21+
http://codesnipers.com/?q=python-flyweights
22+
23+
*TL;DR80
24+
Minimizes memory usage by sharing data with other similar objects.
25+
"""
26+
27+
import weakref
28+
29+
30+
class FlyweightMeta(type):
31+
def __new__(mcs, name, parents, dct):
32+
"""
33+
Set up object pool
34+
35+
:param name: class name
36+
:param parents: class parents
37+
:param dct: dict: includes class attributes, class methods,
38+
static methods, etc
39+
:return: new class
40+
"""
41+
dct['pool'] = weakref.WeakValueDictionary()
42+
return super(FlyweightMeta, mcs).__new__(mcs, name, parents, dct)
43+
44+
@staticmethod
45+
def _serialize_params(cls, *args, **kwargs):
46+
"""
47+
Serialize input parameters to a key.
48+
Simple implementation is just to serialize it as a string
49+
"""
50+
args_list = list(map(str, args))
51+
args_list.extend([str(kwargs), cls.__name__])
52+
key = ''.join(args_list)
53+
return key
54+
55+
def __call__(cls, *args, **kwargs):
56+
key = FlyweightMeta._serialize_params(cls, *args, **kwargs)
57 1241 +
pool = getattr(cls, 'pool', {})
58+
59+
instance = pool.get(key)
60+
if instance is None:
61+
instance = super(FlyweightMeta, cls).__call__(*args, **kwargs)
62+
pool[key] = instance
63+
return instance
64+
65+
66+
class Card(object):
67+
68+
"""The object pool. Has builtin reference counting"""
69+
70+
_CardPool = weakref.WeakValueDictionary()
71+
72+
"""Flyweight implementation. If the object exists in the
73+
pool just return it (instead of creating a new one)"""
74+
75+
def __new__(cls, value, suit):
76+
obj = Card._CardPool.get(value + suit)
77+
if not obj:
78+
obj = object.__new__(cls)
79+
Card._CardPool[value + suit] = obj
80+
obj.value, obj.suit = value, suit
81+
return obj
82+
83+
# def __init__(self, value, suit):
84+
# self.value, self.suit = value, suit
85+
86+
def __repr__(self):
87+
return "<Card: %s%s>" % (self.value, self.suit)
88+
89+
90+
class Card2(metaclass=FlyweightMeta):
91+
def __init__(self, *args, **kwargs):
92+
# print('Init {}: {}'.format(self.__class__, (args, kwargs)))
93+
pass
94+
95+
96+
if __name__ == '__main__':
97+
# comment __new__ and uncomment __init__ to see the difference
98+
c1 = Card('9', 'h')
99+
c2 = Card('9', 'h')
100+
print(c1, c2)
101+
print(c1 == c2, c1 is c2)
102+
print(id(c1), id(c2))
103+
104+
c1.temp = None
105+
c3 = Card('9', 'h')
106+
print(hasattr(c3, 'temp'))
107+
c1 = c2 = c3 = None
108+
c3 = Card('9', 'h')
109+
print(hasattr(c3, 'temp'))
110+
111+
# Tests with metaclass
112+
instances_pool = getattr(Card2, 'pool')
113+
cm1 = Card2('10', 'h', a=1)
114+
cm2 = Card2('10', 'h', a=1)
115+
cm3 = Card2('10', 'h', a=2)
116+
117+
assert (cm1 == cm2) and (cm1 != cm3)
118+
assert (cm1 is cm2) and (cm1 is not cm3)
119+
assert len(instances_pool) == 2
120+
121+
del cm1
122+
assert len(instances_pool) == 2
123+
124+
del cm2
125+
assert len(instances_pool) == 1
126+
127+
del cm3
128+
assert len(instances_pool) == 0
129+
130+
### OUTPUT ###
131+
# (<Card: 9h>, <Card: 9h>)
132+
# (True, True)
133+
# (31903856, 31903856)
134+
# True
135+
# False

tests/structural/test_flyweight.py

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)
0