Prototype is a creational design pattern:
π€π€π€π€π€
What is Prototype?
Prototype is a technique for making the original object copyable.
Why use Prototype?
- Just to copy an object.
- You don’t need to learn Prototype in Python, because we already have a built-in module
copy
to do that.
- Since the built-in module
copy
can clone any object in Python, any object can be a Prototype object.
- The example of override
def __deepcopy__
in refactoring.guru is complicated and incorrect. Don’t copy them. You just need to use the copy module as usual.
Question: So… what is the purpose of this blog?
Answer: Just for knowing that Prototype is not necessary to learn in Python, now skip to another pattern.
When to use Prototype?
Question: When do I use Prototype?
Answer: When you use copy.copy
or copy.deep_copy
, you’re already using Prototype.
- Having an original object
Robot
- Create the copy
copied_robot
from original object original_robot
:
- Coping by (shallow)copy:
- copied_robot.mutable βοΈ original_robot.mutable
- Coping by deep copy:
- copied_robot.mutable π‘οΈ original_robot.mutable
learn more about mutable and immutable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
import copy
class MemoryRobot:
def __init__(self):
self.who_am_i = None
def set_memory(self, who_am_i):
self.who_am_i = who_am_i
class Robot:
def __init__(self, age, body_parts, memory):
self.age = age
self.body_parts = body_parts
self.memory = memory
@property
def id(self):
# https://www.digitalocean.com/community/tutorials/python-id
return str(id(self))[-3:]
if __name__ == "__main__":
def init_robot():
robot_body_parts = [
"head",
{"left hand", "chest", "right hand"},
["left foot", "butt", "right foot"],
]
memory_robot = MemoryRobot()
robot = Robot(18, robot_body_parts, memory_robot)
memory_robot.set_memory(robot)
return robot
def copy_robot(origin_robot, copy_method):
wing = "wing"
tail = "tail"
tab = " "
tab_2 = " " * 2
tab_3 = " " * 3
print(f"\nCoping by {copy_method.__name__}:")
copied_robot = copy_method(origin_robot)
copied_robot.body_parts.append(tail)
is_origin_robot_changed = origin_robot.body_parts[-1] == tail
print(f"{tab}Question: update copied_robot changes origin_robot?")
print(f"{tab_2}Answer: {is_origin_robot_changed}")
origin_robot.body_parts[1].add(wing)
is_copied_robot_changed = wing in copied_robot.body_parts[1]
print(f"{tab}Question: update origin_robot changes copied_robot?")
print(f"{tab_2}Answer: {is_copied_robot_changed}")
print(f"{tab}How about reference?")
print(f"{tab_2}origin_robot reference:")
print(f"{tab_3}{origin_robot.id} is the ID of origin_robot")
print(
f"{tab_3}{origin_robot.memory.who_am_i.id} is the ID of origin_robot.memory.who_am_i"
)
print(f"{tab_2}copied_robot reference:")
print(f"{tab_3}{copied_robot.id} is the ID of copied_robot")
print(
f"{tab_3}{copied_robot.memory.who_am_i.id} is the ID of copied_robot.memory.who_am_i"
)
return copied_robot
origin_robot_1 = init_robot()
shallow_copied_robot = copy_robot(origin_robot_1, copy.copy)
origin_robot_2 = init_robot()
deep_copied_robot = copy_robot(origin_robot_2, copy.deepcopy)
|
Expected Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
Coping by copy:
Question: update copied_robot changes origin_robot?
Answer: True
Question: update origin_robot changes copied_robot?
Answer: True
How about reference?
origin_robot reference:
100 is the ID of origin_robot
100 is the ID of origin_robot.memory.who_am_i
copied_robot reference:
101 is the ID of copied_robot
100 is the ID of copied_robot.memory.who_am_i
Coping by deepcopy:
Question: update copied_robot changes origin_robot?
Answer: False
Question: update origin_robot changes copied_robot?
Answer: False
How about reference?
origin_robot reference:
200 is the ID of origin_robot
200 is the ID of origin_robot.memory.who_am_i
copied_robot reference:
201 is the ID of copied_robot
201 is the ID of copied_robot.memory.who_am_i
|
How to implement Prototype?
Refactoring.guru-Prototype implementation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Robot:
...
def __copy__(self):
body_parts = copy.copy(self.body_parts)
memory = copy.copy(self.memory)
copied_robot = self.__class__(self.age, body_parts, memory)
copied_robot.__dict__.update(self.__dict__)
return copied_robot
def __deepcopy__(self, _=None):
if _ is None:
_ = {}
body_parts = copy.deepcopy(self.body_parts, _)
memory = copy.deepcopy(self.memory, _)
copied_robot = self.__class__(self.age, body_parts, memory)
copied_robot.__dict__ = copy.deepcopy(self.__dict__, _)
return copied_robot
...
|
This implement is incorrect because:
- in the deep copy:
How about reference?
(output line #24
!= #25
)
- copied_robot ID
401
!= copied_robot.memory.who_am_i ID 402
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
Coping by copy:
Question: update copied_robot changes origin_robot?
Answer: True
Question: update origin_robot changes copied_robot?
Answer: True
How about reference?
origin_robot reference:
300 is the ID of origin_robot
300 is the ID of origin_robot.memory.who_am_i
copied_robot reference:
301 is the ID of copied_robot
300 is the ID of copied_robot.memory.who_am_i
Coping by deepcopy:
Question: update copied_robot changes origin_robot?
Answer: False
Question: update origin_robot changes copied_robot?
Answer: False
How about reference?
origin_robot reference:
400 is the ID of origin_robot
400 is the ID of origin_robot.memory.who_am_i
copied_robot reference:
401 is the ID of copied_robot
402 is the ID of copied_robot.memory.who_am_i
|
Source Code: Incorrect Implement
Prototype Implementation:
Youβre already using Prototype from Input:
Source Code: Correct Implement