teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

3

さらに追加検証

2021/06/26 11:04

投稿

neonemo
neonemo

スコア191

answer CHANGED
@@ -1,3 +1,6 @@
1
+ # ★初期回答
2
+ ※ 誤っている点もありますが、記録として残しておきます。後ろに追加した**追加検証**もご覧ください。(誤っている箇所は取り消し線を入れました)
3
+
1
4
  完全に私の主観ですが、こういう事ではないでしょうか。
2
5
 
3
6
  - ① UUIDにより辞書型の**キーが重複しない**様にし
@@ -8,29 +11,36 @@
8
11
  `_id = uuid.uuid4()` について
9
12
  UUID4は乱数ベースなのでデバイスの違いなどによる偏りが出ないと予測します。
10
13
  UUIDを使う意図としては**重複しない事**ですね。辞書型のキーとして使うならここは重要と思います。
11
- データの中身は一緒でもキーが異なるので、同じデータをいくつも持てる事にもなります。
14
+ ~~データの中身は一緒でもキーが異なるので、同じデータをいくつも持てる事にもなります。~~
12
15
 
16
+ ※このプロダクトだとキーは2種類。同一データでもユニークに扱うためのキーと、KV型データの中のキーに相当するものがあります。以降、**ユニークキー**、**データキー**と使い分けます。
13
17
 
14
18
  ## ハッシュ化について
15
19
  `hash(tuple(data))` について
16
20
  データをハッシュ化する事で、一定の型(組込hash()なので整数)になります。
17
- **格納するデータの型はオブジェクトも含んでまちまち**だと思いますが、**一緒かどうかという判定はラク**に出来ると思います。
21
+ ~~格納するデータの型はオブジェクトも含んでまちまちだと思いますが、一緒かどうかという判定はラクに出来ると思います。
18
- キーが違っていても同じデータというのはあるので、それらが一緒かどうかというのもハッシュを見るだけでOKという事になります。
22
+ キーが違っていても同じデータというのはあるので、それらが一緒かどうかというのもハッシュを見るだけでOKという事になります。~~
19
23
 
24
+ ※正しくは、**データキーの重複に容易に気づける事**でしょう
25
+
26
+
20
- ただ、これは要件次第ではデメリットにもなると思っていまして
27
+ ~~ただ、これは要件次第ではデメリットにもなると思っていまして
21
28
  データとしてオブジェクトを格納する場合、一律で`hash(tuple(data))`としているのでオブジェクトの中の特定の値を見たいといった場合には、正しく比較が出来ない可能性もあります。
22
- (例えば`data.hoge`の値だけで同値かを判定する)
29
+ (例えば`data.hoge`の値だけで同値かを判定する)~~
23
30
 
24
- 単に、is演算子的な比較をしたいのかどうかというのもありますね。
31
+ ~~単に、is演算子的な比較をしたいのかどうかというのもありますね。
25
- どちらにせよ、この辺は作りたいものに合わせて修正する必要があるかもしれません。
32
+ どちらにせよ、この辺は作りたいものに合わせて修正する必要があるかもしれません。~~
26
33
 
34
+ ※botterの性質を考えると、デメリットは特に思いつきません
27
35
 
28
36
  ## 蛇足
29
37
  `Java`の話になりますが、全てのObjectにはEquals(obj)という関数があります。
30
38
  これはObject自身のhash値を計算して、引数の`obj`と同値かどうかで判定しています。
31
39
 
32
- 質問に掲載したコードの意図はこれと似たようなものではないのかと思いました。
40
+ ~~質問に掲載したコードの意図はこれと似たようなものではないのかと思いました。~~
33
41
 
42
+ ※機能の性質としてはあっているが、意図とは異なるでしょうね…orz
43
+
34
44
  ## 参考
35
45
  [Python 標準ライブラリ » 組み込み関数](https://docs.python.org/ja/3/library/functions.html#hash)
36
46
  https://docs.python.org/ja/3/library/functions.html#hash
@@ -39,4 +49,95 @@
39
49
  https://docs.python.org/ja/3/library/uuid.html
40
50
 
41
51
  [モジュール java.base > パッケージ java.lang > クラスObject > java.lang.Object](https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object))
42
- https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object)
52
+ https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object)
53
+
54
+
55
+ # ★追加検証分
56
+ 追記分です。
57
+
58
+ 今回掲載したコードで目的を考えるなら、下記の性質から**単サーバ複クライアントでも最新データに気づく為の仕掛け**と考えると腑に落ちました。
59
+
60
+ - ① `DataBase._index`にはキーの数だけ要素が増える事
61
+ - ② `DataBase._data`の各要素に対するindexは、最新のidを指している事
62
+ - ③ GitHubのプロダクトが仮想通貨用のbotterである事(時間とタイミングの勝負でしょうし)
63
+
64
+
65
+ ## 検証コード
66
+ ```python3
67
+ from typing import Dict
68
+ import uuid
69
+
70
+ class DataBase:
71
+
72
+ def __init__(self) -> None:
73
+ """
74
+ 初期化
75
+ """
76
+ self._data: Dict[uuid.UUID, Dict] = {}
77
+ self._index: Dict[int, uuid.UUID] = {}
78
+
79
+
80
+ def insert(self, data: Dict[str, int]) -> None:
81
+ """
82
+ KV型データを追加する
83
+ """
84
+ _id = uuid.uuid4()
85
+ self._data[_id] = data
86
+
87
+ keyhash = hash(tuple(data))
88
+ self._index[keyhash] = _id
89
+
90
+ def print_all_datas(self):
91
+ """
92
+ データリストを出力する
93
+ """
94
+ for i, _id in enumerate(self._data):
95
+ _data = self._data[_id]
96
+ _seed = tuple(_data)
97
+ _keyhash = hash(_seed)
98
+ _index = self._index[_keyhash]
99
+ print(f'{i} => id: {_id}, index: {_index}, hash: {_keyhash: >20}, seed: {_seed}, data: {_data}')
100
+
101
+ def print_all_indexes(self):
102
+ """
103
+ ハッシュリストを出力する
104
+ """
105
+ for i, _keyhash in enumerate(self._index):
106
+ _id = self._index[_keyhash]
107
+ print(f'{i} => hash: {_keyhash: >20}, latest_id: {_id}')
108
+
109
+ db = DataBase()
110
+ db.insert({'priceA': 5000})
111
+ db.insert({'priceB': 4000})
112
+ db.insert({'priceA': 3000})
113
+ db.insert({'priceA': 5000})
114
+ db.insert({'priceA': 7000})
115
+
116
+ print('### print_all_datas ###')
117
+ db.print_all_datas()
118
+
119
+ print('### print_all_indexes ###')
120
+ db.print_all_indexes()
121
+ ```
122
+
123
+ ## 実行結果
124
+ ```log
125
+ ### print_all_datas ###
126
+ 0 => id: 498a2423-8f65-42ac-90f7-d2e321f8b1ec, index: 689e09cb-dadd-4cb7-ad69-a6377c4a362f, hash: -261320819014024164, seed: ('priceA',), data: {'priceA': 5000}
127
+ 1 => id: daaa98fb-cba8-4dbd-9299-172754780f01, index: daaa98fb-cba8-4dbd-9299-172754780f01, hash: 4814718565598271203, seed: ('priceB',), data: {'priceB': 4000}
128
+ 2 => id: c0422535-cdae-4b92-a22a-09cf98dd928f, index: 689e09cb-dadd-4cb7-ad69-a6377c4a362f, hash: -261320819014024164, seed: ('priceA',), data: {'priceA': 3000}
129
+ 3 => id: cf0d74f1-752f-43a6-898c-292c276d9bb3, index: 689e09cb-dadd-4cb7-ad69-a6377c4a362f, hash: -261320819014024164, seed: ('priceA',), data: {'priceA': 5000}
130
+ 4 => id: 689e09cb-dadd-4cb7-ad69-a6377c4a362f, index: 689e09cb-dadd-4cb7-ad69-a6377c4a362f, hash: -261320819014024164, seed: ('priceA',), data: {'priceA': 7000}
131
+ ### print_all_indexes ###
132
+ 0 => hash: -261320819014024164, latest_id: 689e09cb-dadd-4cb7-ad69-a6377c4a362f
133
+ 1 => hash: 4814718565598271203, latest_id: daaa98fb-cba8-4dbd-9299-172754780f01
134
+ ```
135
+
136
+ ## 検証結果から分かる事
137
+ - 登録したデータは重複キーも含めて全てユニークに持っている(UUIDをユニークキーとしている)
138
+ - indexには、最新データを持つユニークキーの一覧が入っている
139
+ - 各登録データは、hashを使ったデータキーによるアクセスをする事で、indexに入っている最新のユニークキーが事に気づける。クライアントとしては**自分のユニークキーとは違う**と気づける
140
+ - 今回の掲載コードにはありませんが、hashが一致しなければ更新させないといった**楽観的ロックの仕掛け**と見る事も出来そうですね
141
+
142
+
143
+ 以上です。長文失礼しました。

2

コード要らないか…

2021/06/26 11:04

投稿

neonemo
neonemo

スコア191

answer CHANGED
@@ -21,19 +21,6 @@
21
21
  データとしてオブジェクトを格納する場合、一律で`hash(tuple(data))`としているのでオブジェクトの中の特定の値を見たいといった場合には、正しく比較が出来ない可能性もあります。
22
22
  (例えば`data.hoge`の値だけで同値かを判定する)
23
23
 
24
- ```python
25
- data1 = { hoge: 123, fuga: 'piyo1' }
26
- data2 = { hoge: 123, fuga: 'piyo2' }
27
- ```
28
- といったデータがある時、どちらの比較を採用するのかといった感じです。
29
- ```python
30
- def equals1(a, b):
31
- return hash(tuple(a)) == hash(tuple(b))
32
-
33
- def equals2(a, b):
34
- return a.hoge == b.hoge
35
- ```
36
-
37
24
  単に、is演算子的な比較をしたいのかどうかというのもありますね。
38
25
  どちらにせよ、この辺は作りたいものに合わせて修正する必要があるかもしれません。
39
26
 

1

質問が素晴らしいのでコメントを入れるまでもないか…

2021/06/26 01:12

投稿

neonemo
neonemo

スコア191

answer CHANGED
@@ -28,11 +28,9 @@
28
28
  といったデータがある時、どちらの比較を採用するのかといった感じです。
29
29
  ```python
30
30
  def equals1(a, b):
31
- # falseになる
32
31
  return hash(tuple(a)) == hash(tuple(b))
33
32
 
34
33
  def equals2(a, b):
35
- # trueになる
36
34
  return a.hoge == b.hoge
37
35
  ```
38
36