目次
辞書データ構造分析
#define GROWTH_RATE(d) (((d)->ma_used*2)+((d)->ma_keys->dk_size>>1))
ログイン後にコピー
" >
#define GROWTH_RATE(d) (((d)->ma_used*2)+((d)->ma_keys->dk_size>>1))
ログイン後にコピー
ホームページ バックエンド開発 Python チュートリアル Python仮想マシンにおける辞書の実装原理は何ですか

Python仮想マシンにおける辞書の実装原理は何ですか

May 19, 2023 pm 08:19 PM
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;
ログイン後にコピー

Python仮想マシンにおける辞書の実装原理は何ですか

上記の各フィールドの意味は次のとおりです:

  • 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、ハッシュ テーブル。キーと値のペアが実際に保存されます。

  • #ハッシュ テーブル全体のレイアウトは次のようになります。

#新しい辞書オブジェクトの作成Python仮想マシンにおける辞書の実装原理は何ですか

This この関数はまだ比較的単純で、最初にメモリ空間を適用し、次にいくつかの初期化操作を実行し、キーと値のペアを保存するためのハッシュ テーブルを適用します。

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 に達します。この時点で、それを拡張する必要があります。拡張後の配列のサイズは次のように計算されます:

#define GROWTH_RATE(d) (((d)->ma_used*2)+((d)->ma_keys->dk_size>>1))
ログイン後にコピー

新しい配列のサイズは次のようになります。元のキーと値のペアの数に 2 を乗算し、元の配列の長さの半分を加えた値。

一般に、拡張には 3 つの主な手順があります。

新しい配列のサイズを計算します。
  • 新しい配列を作成します。
  • 元のハッシュ テーブルのデータを新しい配列に追加します (つまり、再ハッシュ プロセス)。
  • 具体的な実装コードは次のとおりです。
  • 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 サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

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

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

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

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

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

Debian Apacheログを使用してWebサイトのパフォーマンスを向上させる方法 Debian Apacheログを使用してWebサイトのパフォーマンスを向上させる方法 Apr 12, 2025 pm 11:36 PM

この記事では、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など Python:ゲーム、GUIなど Apr 13, 2025 am 12:14 AM

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

PHPとPython:2つの一般的なプログラミング言語を比較します PHPとPython:2つの一般的なプログラミング言語を比較します Apr 14, 2025 am 12:13 AM

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

Debian Readdirが他のツールと統合する方法 Debian Readdirが他のツールと統合する方法 Apr 13, 2025 am 09:42 AM

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

DDOS攻撃検出におけるDebianスニファーの役割 DDOS攻撃検出におけるDebianスニファーの役割 Apr 12, 2025 pm 10:42 PM

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

Pythonと時間:勉強時間を最大限に活用する Pythonと時間:勉強時間を最大限に活用する Apr 14, 2025 am 12:02 AM

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

Nginx SSL証明書更新Debianチュートリアル Nginx SSL証明書更新Debianチュートリアル Apr 13, 2025 am 07:21 AM

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

debian opensslでHTTPSサーバーを構成する方法 debian opensslでHTTPSサーバーを構成する方法 Apr 13, 2025 am 11:03 AM

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

See all articles