#define GROWTH_RATE(d) (((d)->ma_used*2)+((d)->ma_keys->dk_size>>1))
Python仮想マシンにおける辞書の実装原理は何ですか
辞書データ構造分析
/* The ma_values pointer is NULL for a combined table * or points to an array of PyObject* for a split table */ typedef struct { PyObject_HEAD Py_ssize_t ma_used; PyDictKeysObject *ma_keys; PyObject **ma_values; } PyDictObject; struct _dictkeysobject { Py_ssize_t dk_refcnt; Py_ssize_t dk_size; dict_lookup_func dk_lookup; Py_ssize_t dk_usable; PyDictKeyEntry dk_entries[1]; }; typedef struct { /* Cached hash code of me_key. */ Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; /* This field is only meaningful for combined tables */ } PyDictKeyEntry;
上記の各フィールドの意味は次のとおりです:
- ob_refcnt、オブジェクトの参照カウント。
- ob_type、オブジェクトのデータ型。
- ma_used、現在のハッシュ テーブル内のデータの数。
- ma_keys は、キーと値のペアを保持する配列を指します。
- ma_values、これは値の配列を指しますが、_dictkeysobject の PyDictKeyEntry 配列内のオブジェクトも値を格納できるため、この値は必ずしも cpython の特定の実装で使用されるわけではありません。この値は、すべてのキーが文字列である場合にのみ使用できます。この記事では、PyDictKeyEntry の値は主に辞書の実装について説明するために使用されるため、この変数は無視してかまいません。
- dk_refcnt, これは参照カウントを表すためにも使用されます。これは辞書ビューに関連しています。原理は参照カウントと似ているため、ここでは無視します。
- dk_size、これはハッシュ テーブルのサイズを表し、2n である必要があります。この場合、モジュラー演算はビット単位の AND 演算に変えることができます。
- dk_lookup、これはハッシュ テーブルのルックアップ関数を表し、関数ポインターです。
- dk_usable は、現在の配列で使用できるキーと値のペアの数を示します。
- dk_entries、ハッシュ テーブル。キーと値のペアが実際に保存されます。 #ハッシュ テーブル全体のレイアウトは次のようになります。
#新しい辞書オブジェクトの作成
static PyObject *
dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *self;
PyDictObject *d;
assert(type != NULL && type->tp_alloc != NULL);
// 申请内存空间
self = type->tp_alloc(type, 0);
if (self == NULL)
return NULL;
d = (PyDictObject *)self;
/* The object has been implicitly tracked by tp_alloc */
if (type == &PyDict_Type)
_PyObject_GC_UNTRACK(d);
// 因为还没有增加数据 因此哈希表当中 ma_used = 0
d->ma_used = 0;
// 申请保存键值对的数组 PyDict_MINSIZE_COMBINED 是一个宏定义 值为 8 表示哈希表数组的最小长度
d->ma_keys = new_keys_object(PyDict_MINSIZE_COMBINED);
// 如果申请失败返回 NULL
if (d->ma_keys == NULL) {
Py_DECREF(self);
return NULL;
}
return self;
}
// new_keys_object 函数如下所示
static PyDictKeysObject *new_keys_object(Py_ssize_t size)
{
PyDictKeysObject *dk;
Py_ssize_t i;
PyDictKeyEntry *ep0;
assert(size >= PyDict_MINSIZE_SPLIT);
assert(IS_POWER_OF_2(size));
// 这里是申请内存的位置真正申请内存空间的大小为 PyDictKeysObject 的大小加上 size-1 个PyDictKeyEntry的大小
// 这里你可能会有一位为啥不是 size 个 PyDictKeyEntry 的大小 因为在结构体 PyDictKeysObject 当中已经申请了一个 PyDictKeyEntry 对象了
dk = PyMem_MALLOC(sizeof(PyDictKeysObject) +
sizeof(PyDictKeyEntry) * (size-1));
if (dk == NULL) {
PyErr_NoMemory();
return NULL;
}
// 下面主要是一些初始化的操作 dk_refcnt 设置成 1 因为目前只有一个字典对象使用 这个 PyDictKeysObject 对象
DK_DEBUG_INCREF dk->dk_refcnt = 1;
dk->dk_size = size; // 哈希表的大小
// 下面这行代码主要是表示哈希表当中目前还能存储多少个键值对 在 cpython 的实现当中允许有 2/3 的数组空间去存储数据 超过这个数则需要进行扩容
dk->dk_usable = USABLE_FRACTION(size); // #define USABLE_FRACTION(n) ((((n) << 1)+1)/3)
ep0 = &dk->dk_entries[0];
/* Hash value of slot 0 is used by popitem, so it must be initialized */
ep0->me_hash = 0;
// 将所有的键值对初始化成 NULL
for (i = 0; i < size; i++) {
ep0[i].me_key = NULL;
ep0[i].me_value = NULL;
}
dk->dk_lookup = lookdict_unicode_nodummy;
return dk;
}
ログイン後にコピー
ハッシュ テーブルの拡張メカニズムまず、ディクショナリ実装におけるハッシュ テーブルの拡張メカニズムを見てみましょう。ディクショナリに新しいデータを追加し続けると、ディクショナリは間もなく、その中のデータは配列の長さの 23 に達します。この時点で、それを拡張する必要があります。拡張後の配列のサイズは次のように計算されます: static PyObject * dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *self; PyDictObject *d; assert(type != NULL && type->tp_alloc != NULL); // 申请内存空间 self = type->tp_alloc(type, 0); if (self == NULL) return NULL; d = (PyDictObject *)self; /* The object has been implicitly tracked by tp_alloc */ if (type == &PyDict_Type) _PyObject_GC_UNTRACK(d); // 因为还没有增加数据 因此哈希表当中 ma_used = 0 d->ma_used = 0; // 申请保存键值对的数组 PyDict_MINSIZE_COMBINED 是一个宏定义 值为 8 表示哈希表数组的最小长度 d->ma_keys = new_keys_object(PyDict_MINSIZE_COMBINED); // 如果申请失败返回 NULL if (d->ma_keys == NULL) { Py_DECREF(self); return NULL; } return self; } // new_keys_object 函数如下所示 static PyDictKeysObject *new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t i; PyDictKeyEntry *ep0; assert(size >= PyDict_MINSIZE_SPLIT); assert(IS_POWER_OF_2(size)); // 这里是申请内存的位置真正申请内存空间的大小为 PyDictKeysObject 的大小加上 size-1 个PyDictKeyEntry的大小 // 这里你可能会有一位为啥不是 size 个 PyDictKeyEntry 的大小 因为在结构体 PyDictKeysObject 当中已经申请了一个 PyDictKeyEntry 对象了 dk = PyMem_MALLOC(sizeof(PyDictKeysObject) + sizeof(PyDictKeyEntry) * (size-1)); if (dk == NULL) { PyErr_NoMemory(); return NULL; } // 下面主要是一些初始化的操作 dk_refcnt 设置成 1 因为目前只有一个字典对象使用 这个 PyDictKeysObject 对象 DK_DEBUG_INCREF dk->dk_refcnt = 1; dk->dk_size = size; // 哈希表的大小 // 下面这行代码主要是表示哈希表当中目前还能存储多少个键值对 在 cpython 的实现当中允许有 2/3 的数组空间去存储数据 超过这个数则需要进行扩容 dk->dk_usable = USABLE_FRACTION(size); // #define USABLE_FRACTION(n) ((((n) << 1)+1)/3) ep0 = &dk->dk_entries[0]; /* Hash value of slot 0 is used by popitem, so it must be initialized */ ep0->me_hash = 0; // 将所有的键值对初始化成 NULL for (i = 0; i < size; i++) { ep0[i].me_key = NULL; ep0[i].me_value = NULL; } dk->dk_lookup = lookdict_unicode_nodummy; return dk; }
#define GROWTH_RATE(d) (((d)->ma_used*2)+((d)->ma_keys->dk_size>>1))
ログイン後にコピー
新しい配列のサイズは次のようになります。元のキーと値のペアの数に 2 を乗算し、元の配列の長さの半分を加えた値。 一般に、拡張には 3 つの主な手順があります。 新しい配列のサイズを計算します。 #define GROWTH_RATE(d) (((d)->ma_used*2)+((d)->ma_keys->dk_size>>1))
- 新しい配列を作成します。
- 元のハッシュ テーブルのデータを新しい配列に追加します (つまり、再ハッシュ プロセス)。
- 具体的な実装コードは次のとおりです。
static int insertion_resize(PyDictObject *mp) { return dictresize(mp, GROWTH_RATE(mp)); } static int dictresize(PyDictObject *mp, Py_ssize_t minused) { Py_ssize_t newsize; PyDictKeysObject *oldkeys; PyObject **oldvalues; Py_ssize_t i, oldsize; // 下面的代码的主要作用就是计算得到能够大于等于 minused 最小的 2 的整数次幂 /* Find the smallest table size > minused. */ for (newsize = PyDict_MINSIZE_COMBINED; newsize <= minused && newsize > 0; newsize <<= 1) ; if (newsize <= 0) { PyErr_NoMemory(); return -1; } oldkeys = mp->ma_keys; oldvalues = mp->ma_values; /* Allocate a new table. */ // 创建新的数组 mp->ma_keys = new_keys_object(newsize); if (mp->ma_keys == NULL) { mp->ma_keys = oldkeys; return -1; } if (oldkeys->dk_lookup == lookdict) mp->ma_keys->dk_lookup = lookdict; oldsize = DK_SIZE(oldkeys); mp->ma_values = NULL; /* If empty then nothing to copy so just return */ if (oldsize == 1) { assert(oldkeys == Py_EMPTY_KEYS); DK_DECREF(oldkeys); return 0; } /* Main loop below assumes we can transfer refcount to new keys * and that value is stored in me_value. * Increment ref-counts and copy values here to compensate * This (resizing a split table) should be relatively rare */ if (oldvalues != NULL) { for (i = 0; i < oldsize; i++) { if (oldvalues[i] != NULL) { Py_INCREF(oldkeys->dk_entries[i].me_key); oldkeys->dk_entries[i].me_value = oldvalues[i]; } } } /* Main loop */ // 将原来数组当中的元素加入到新的数组当中 for (i = 0; i < oldsize; i++) { PyDictKeyEntry *ep = &oldkeys->dk_entries[i]; if (ep->me_value != NULL) { assert(ep->me_key != dummy); insertdict_clean(mp, ep->me_key, ep->me_hash, ep->me_value); } } // 更新一下当前哈希表当中能够插入多少数据 mp->ma_keys->dk_usable -= mp->ma_used; if (oldvalues != NULL) { /* NULL out me_value slot in oldkeys, in case it was shared */ for (i = 0; i < oldsize; i++) oldkeys->dk_entries[i].me_value = NULL; assert(oldvalues != empty_values); free_values(oldvalues); DK_DECREF(oldkeys); } else { assert(oldkeys->dk_lookup != lookdict_split); if (oldkeys->dk_lookup != lookdict_unicode_nodummy) { PyDictKeyEntry *ep0 = &oldkeys->dk_entries[0]; for (i = 0; i < oldsize; i++) { if (ep0[i].me_key == dummy) Py_DECREF(dummy); } } assert(oldkeys->dk_refcnt == 1); DK_DEBUG_DECREF PyMem_FREE(oldkeys); } return 0; }
以上がPython仮想マシンにおける辞書の実装原理は何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









この記事では、Debianシステムの下でApacheログを分析することにより、Webサイトのパフォーマンスを改善する方法について説明します。 1.ログ分析の基本Apacheログは、IPアドレス、タイムスタンプ、リクエストURL、HTTPメソッド、応答コードなど、すべてのHTTP要求の詳細情報を記録します。 Debian Systemsでは、これらのログは通常、/var/log/apache2/access.logおよび/var/log/apache2/error.logディレクトリにあります。ログ構造を理解することは、効果的な分析の最初のステップです。 2。ログ分析ツールさまざまなツールを使用してApacheログを分析できます。コマンドラインツール:GREP、AWK、SED、およびその他のコマンドラインツール。

PythonはゲームとGUI開発に優れています。 1)ゲーム開発は、2Dゲームの作成に適した図面、オーディオ、その他の機能を提供し、Pygameを使用します。 2)GUI開発は、TKINTERまたはPYQTを選択できます。 TKINTERはシンプルで使いやすく、PYQTは豊富な機能を備えており、専門能力開発に適しています。

PHPとPythonにはそれぞれ独自の利点があり、プロジェクトの要件に従って選択します。 1.PHPは、特にWebサイトの迅速な開発とメンテナンスに適しています。 2。Pythonは、データサイエンス、機械学習、人工知能に適しており、簡潔な構文を備えており、初心者に適しています。

DebianシステムのReadDir関数は、ディレクトリコンテンツの読み取りに使用されるシステムコールであり、Cプログラミングでよく使用されます。この記事では、ReadDirを他のツールと統合して機能を強化する方法について説明します。方法1:C言語プログラムを最初にパイプラインと組み合わせて、cプログラムを作成してreaddir関数を呼び出して結果をinclude#include#include inctargc、char*argv []){dir*dir; structdireant*entry; if(argc!= 2){(argc!= 2){

この記事では、DDOS攻撃検出方法について説明します。 「DebiansNiffer」の直接的なアプリケーションのケースは見つかりませんでしたが、次の方法はDDOS攻撃検出に使用できます:効果的なDDOS攻撃検出技術:トラフィック分析に基づく検出:突然のトラフィックの成長、特定のポートの接続の急増などのネットワークトラフィックの異常なパターンの識別。たとえば、PysharkライブラリとColoramaライブラリと組み合わせたPythonスクリプトは、ネットワークトラフィックをリアルタイムで監視し、アラートを発行できます。統計分析に基づく検出:データなどのネットワークトラフィックの統計的特性を分析することにより

限られた時間でPythonの学習効率を最大化するには、PythonのDateTime、時間、およびスケジュールモジュールを使用できます。 1. DateTimeモジュールは、学習時間を記録および計画するために使用されます。 2。時間モジュールは、勉強と休息の時間を設定するのに役立ちます。 3.スケジュールモジュールは、毎週の学習タスクを自動的に配置します。

この記事では、DebianシステムでNGINXSSL証明書を更新する方法について説明します。ステップ1:最初にCERTBOTをインストールして、システムがCERTBOTおよびPython3-Certbot-Nginxパッケージがインストールされていることを確認してください。インストールされていない場合は、次のコマンドを実行してください。sudoapt-getupdatesudoapt-getinstolcallcertbotthon3-certbot-nginxステップ2:certbotコマンドを取得して構成してlet'sencrypt証明書を取得し、let'sencryptコマンドを取得し、nginx:sudocertbot - nginxを構成します。

DebianシステムでHTTPSサーバーの構成には、必要なソフトウェアのインストール、SSL証明書の生成、SSL証明書を使用するWebサーバー(ApacheやNginxなど)の構成など、いくつかのステップが含まれます。 Apachewebサーバーを使用していると仮定して、基本的なガイドです。 1.最初に必要なソフトウェアをインストールし、システムが最新であることを確認し、ApacheとOpenSSL:sudoaptupdatesudoaptupgraysudoaptinstaをインストールしてください
