目次
1 Unicode
2 Python における Unicode
2.1 Unicode オブジェクトの利点
3 Unicode对象的底层结构体
3.1 PyASCIIObject
3.2 PyCompactUnicodeObject
3.3 PyUnicodeObject
3.4 示例
4 interned机制
ホームページ バックエンド開発 Python チュートリアル Python組み込み型strソースコード解析

Python組み込み型strソースコード解析

May 09, 2023 pm 02:16 PM
python str

1 Unicode

コンピュータ ストレージの基本単位は 8 ビットで構成されるバイトです。英語は 26 文字といくつかの記号のみで構成されているため、英語の文字はバイト単位で直接格納できます。ただし、他の言語 (中国語、日本語、韓国語など) では、文字数が多いため、エンコードに複数のバイトを使用する必要があります。

コンピューター技術の普及に伴い、非ラテン文字エンコーディング技術は発展を続けていますが、依然として 2 つの大きな制限があります。

  • 複数の言語をサポートしていません。ある言語のエンコード スキームを別の言語に使用することはできません

  • 統一された標準はありません。たとえば、中国語には GBK、GB2312、GB18030

  • などの複数のエンコード標準があります。

エンコード方式が統一されていないため、開発者は異なるエンコード間で変換を行ったり来たりする必要があり、必然的に多くのエラーが発生します。このような不一致の問題を解決するために、Unicode 標準が提案されました。 Unicode は、世界中のほとんどの書記体系を整理してエンコードし、コンピュータが統一された方法でテキストを処理できるようにします。 Unicode には現在 140,000 文字以上が含まれており、当然ながら複数の言語をサポートしています。 (Unicode の uni は「unification」の語源です)

2 Python における Unicode

2.1 Unicode オブジェクトの利点

Python 3 以降、Unicode は str オブジェクトの内部で使用されます。を表すため、ソース コードでは Unicode オブジェクトになります。 Unicode 表現を使用する利点は、プログラムのコア ロジックが Unicode を均一に使用し、入力層と出力層でのみデコードおよびエンコードする必要があるため、さまざまなエンコードの問題を最大限に回避できることです。

図は次のとおりです:

Python組み込み型strソースコード解析

##2.2 Python による Unicode の最適化

問題: Unicode には 140,000 を超える文字が含まれているため、それぞれの A 文字は保存するには少なくとも 4 バイトが必要です (これはおそらく 2 バイトでは不十分であるため、4 バイトが使用され、3 バイトは通常は使用されません)。英語文字の ASCII コードには 1 バイトしか必要ありませんが、Unicode を使用すると、頻繁に使用される英語文字のコストが 4 倍になります。

まず、Python のさまざまな形式の str オブジェクトのサイズの違いを見てみましょう:

>>> sys.getsizeof('ab') - sys.getsizeof('a')
1
>>> sys.getsizeof('一二') - sys.getsizeof('一')
2
>>> sys.getsizeof('????????') - sys.getsizeof('????')
4
ログイン後にコピー

テキストの内容に従って、Python が内部的に Unicode オブジェクトを最適化していることがわかります。 、基礎となるストレージユニットが選択されます。

Unicode オブジェクトの基礎となるストレージは、テキスト文字の Unicode コード ポイント範囲に従って 3 つのカテゴリに分類されます:

  • PyUnicode_1BYTE_KIND: すべての文字コード ポイントは U 0000 の間にあります。および U 00FF

  • PyUnicode_2BYTE_KIND: すべての文字コード ポイントが U 0000 から U FFFF の間にあり、少なくとも 1 つの文字のコード ポイントが U 00FF

    ## より大きい
  • #PyUnicode_1BYTE_KIND: すべての文字コード ポイントは U 0000 ~ U 10FFFF であり、少なくとも 1 つの文字のコード ポイントが U FFFF
  • ##対応する列挙は次のとおりです。 ##
    enum PyUnicode_Kind {
    /* String contains only wstr byte characters.  This is only possible
       when the string was created with a legacy API and _PyUnicode_Ready()
       has not been called yet.  */
        PyUnicode_WCHAR_KIND = 0,
    /* Return values of the PyUnicode_KIND() macro: */
        PyUnicode_1BYTE_KIND = 1,
        PyUnicode_2BYTE_KIND = 2,
        PyUnicode_4BYTE_KIND = 4
    };
    ログイン後にコピー
  • さまざまな分類に従って、さまざまなストレージ ユニットを選択します:
/* Py_UCS4 and Py_UCS2 are typedefs for the respective
   unicode representations. */
typedef uint32_t Py_UCS4;
typedef uint16_t Py_UCS2;
typedef uint8_t Py_UCS1;
ログイン後にコピー

対応する関係は次のとおりです:

テキスト タイプ文字ストレージ ユニットPy_UCS1Py_UCS2Py_UCS4Unicode の内部ストレージ構造はテキスト タイプによって異なるため、タイプの種類は Unicode オブジェクトのパブリック フィールドとして保存する必要があります。 Python は内部的にいくつかのフラグ ビットを Unicode パブリック フィールドとして定義します: (作成者のレベルが限られているため、ここにあるすべてのフィールドは後続のコンテンツでは紹介されません。これについては後ほど自分で学ぶことができます。頑張ってください~)
文字ストレージ ユニット サイズ (バイト)PyUnicode_1BYTE_KIND
1 ##PyUnicode_2BYTE_KIND
2PyUnicode_4BYTE_KIND
4

interned: interned メカニズムを維持するかどうか

  • kind: type、基礎となる文字の記憶単位のサイズを区別するために使用されます

  • compact: メモリ割り当て方法、オブジェクトとテキスト バッファーが分離されているかどうか

  • asscii: テキストがすべて純粋な ASCII かどうか

  • PyUnicode_New 関数を通じて、テキスト文字数のサイズと最大文字数に従って、maxchar が Unicode オブジェクトを初期化します。この関数は主に、maxchar に基づいて最もコンパクトな文字格納ユニットと Unicode オブジェクトの基礎となる構造を選択します。 (ソース コードは比較的長いため、ここには記載しません。ご自身で理解してください。以下の表形式で示します) )

maxchar < 128kindPyUnicode_1BYTE_KIND11PyASCIIObject
128 <= maxchar < 256256 <= maxchar < 65536 65536 <= maxchar < MAX_UNICODE
PyUnicode_1BYTE_KIND PyUnicode_2BYTE_KINDPyUnicode_4BYTE_KINDascii
000文字格納単位サイズ (バイト)
124 基礎構造
PyCompactUnicodeObjectPyCompactUnicodeObjectPyCompactUnicodeObject

3 Unicode对象的底层结构体

3.1 PyASCIIObject

C源码:

typedef struct {
    PyObject_HEAD
    Py_ssize_t length;          /* Number of code points in the string */
    Py_hash_t hash;             /* Hash value; -1 if not set */
    struct {
        unsigned int interned:2;
        unsigned int kind:3;
        unsigned int compact:1;
        unsigned int ascii:1;
        unsigned int ready:1;
        unsigned int :24;
    } state;
    wchar_t *wstr;              /* wchar_t representation (null-terminated) */
} PyASCIIObject;
ログイン後にコピー

源码分析:

length:文本长度

hash:文本哈希值

state:Unicode对象标志位

wstr:缓存C字符串的一个wchar_t指针,以“\0”结束(这里和我看的另一篇文章讲得不太一样,另一个描述是:ASCII文本紧接着位于PyASCIIObject结构体后面,我个人觉得现在的这种说法比较准确,毕竟源码结构体后面没有别的字段了)

图示如下:

(注意这里state字段后面有一个4字节大小的空洞,这是结构体字段内存对齐造成的现象,主要是为了优化内存访问效率)

Python組み込み型strソースコード解析

ASCII文本由wstr指向,以’abc’和空字符串对象’'为例:

Python組み込み型strソースコード解析

Python組み込み型strソースコード解析

3.2 PyCompactUnicodeObject

如果文本不全是ASCII,Unicode对象底层便由PyCompactUnicodeObject结构体保存。C源码如下:

/* Non-ASCII strings allocated through PyUnicode_New use the
   PyCompactUnicodeObject structure. state.compact is set, and the data
   immediately follow the structure. */
typedef struct {
    PyASCIIObject _base;
    Py_ssize_t utf8_length;     /* Number of bytes in utf8, excluding the
                                 * terminating \0. */
    char *utf8;                 /* UTF-8 representation (null-terminated) */
    Py_ssize_t wstr_length;     /* Number of code points in wstr, possible
                                 * surrogates count as two code points. */
} PyCompactUnicodeObject;
ログイン後にコピー

PyCompactUnicodeObject在PyASCIIObject的基础上增加了3个字段:

utf8_length:文本UTF8编码长度

utf8:文本UTF8编码形式,缓存以避免重复编码运算

wstr_length:wstr的“长度”(这里所谓的长度没有找到很准确的说法,笔者也不太清楚怎么能打印出来,大家可以自行研究下)

注意到,PyASCIIObject中并没有保存UTF8编码形式,这是因为ASCII本身就是合法的UTF8,这也是ASCII文本底层由PyASCIIObject保存的原因。

结构图示:

Python組み込み型strソースコード解析

3.3 PyUnicodeObject

PyUnicodeObject则是Python中str对象的具体实现。C源码如下:

/* Strings allocated through PyUnicode_FromUnicode(NULL, len) use the
   PyUnicodeObject structure. The actual string data is initially in the wstr
   block, and copied into the data block using _PyUnicode_Ready. */
typedef struct {
    PyCompactUnicodeObject _base;
    union {
        void *any;
        Py_UCS1 *latin1;
        Py_UCS2 *ucs2;
        Py_UCS4 *ucs4;
    } data;                     /* Canonical, smallest-form Unicode buffer */
} PyUnicodeObject;
ログイン後にコピー

3.4 示例

在日常开发时,要结合实际情况注意字符串拼接前后的内存大小差别:

>>> import sys
>>> text = &#39;a&#39; * 1000
>>> sys.getsizeof(text)
1049
>>> text += &#39;????&#39;
>>> sys.getsizeof(text)
4080
ログイン後にコピー

4 interned机制

如果str对象的interned标志位为1,Python虚拟机将为其开启interned机制,

源码如下:(相关信息在网上可以看到很多说法和解释,这里笔者能力有限,暂时没有找到最确切的答案,之后补充。抱拳~但是我们通过分析源码应该是能看出一些门道的)

/* This dictionary holds all interned unicode strings.  Note that references
   to strings in this dictionary are *not* counted in the string&#39;s ob_refcnt.
   When the interned string reaches a refcnt of 0 the string deallocation
   function will delete the reference from this dictionary.
   Another way to look at this is that to say that the actual reference
   count of a string is:  s->ob_refcnt + (s->state ? 2 : 0)
*/
static PyObject *interned = NULL;
void
PyUnicode_InternInPlace(PyObject **p)
{
    PyObject *s = *p;
    PyObject *t;
#ifdef Py_DEBUG
    assert(s != NULL);
    assert(_PyUnicode_CHECK(s));
#else
    if (s == NULL || !PyUnicode_Check(s))
        return;
#endif
    /* If it&#39;s a subclass, we don&#39;t really know what putting
       it in the interned dict might do. */
    if (!PyUnicode_CheckExact(s))
        return;
    if (PyUnicode_CHECK_INTERNED(s))
        return;
    if (interned == NULL) {
        interned = PyDict_New();
        if (interned == NULL) {
            PyErr_Clear(); /* Don&#39;t leave an exception */
            return;
        }
    }
    Py_ALLOW_RECURSION
    t = PyDict_SetDefault(interned, s, s);
    Py_END_ALLOW_RECURSION
    if (t == NULL) {
        PyErr_Clear();
        return;
    }
    if (t != s) {
        Py_INCREF(t);
        Py_SETREF(*p, t);
        return;
    }
    /* The two references in interned are not counted by refcnt.
       The deallocator will take care of this */
    Py_REFCNT(s) -= 2;
    _PyUnicode_STATE(s).interned = SSTATE_INTERNED_MORTAL;
}
ログイン後にコピー

可以看到,源码前面还是做一些基本的检查。我们可以看一下37行和50行:将s添加到interned字典中时,其实s同时是key和value(这里我不太清楚为什么会这样做),所以s对应的引用计数是+2了的(具体可以看PyDict_SetDefault()的源码),所以在50行时会将计数-2,保证引用计数的正确。

考虑下面的场景:

>>> class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age
>>> user = User(&#39;Tom&#39;, 21)
>>> user.__dict__
{&#39;name&#39;: &#39;Tom&#39;, &#39;age&#39;: 21}
ログイン後にコピー

由于对象的属性由dict保存,这意味着每个User对象都要保存一个str对象‘name’,这会浪费大量的内存。而str是不可变对象,因此Python内部将有潜在重复可能的字符串都做成单例模式,这就是interned机制。Python具体做法就是在内部维护一个全局dict对象,所有开启interned机制的str对象均保存在这里,后续需要使用的时候,先创建,如果判断已经维护了相同的字符串,就会将新创建的这个对象回收掉。

示例:

由不同运算生成’abc’,最后都是同一个对象:

>>> a = &#39;abc&#39;
>>> b = &#39;ab&#39; + &#39;c&#39;
>>> id(a), id(b), a is b
(2752416949872, 2752416949872, True)
ログイン後にコピー

以上がPython組み込み型strソースコード解析の詳細内容です。詳細については、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)

mysqlは支払う必要がありますか mysqlは支払う必要がありますか Apr 08, 2025 pm 05:36 PM

MySQLには、無料のコミュニティバージョンと有料エンタープライズバージョンがあります。コミュニティバージョンは無料で使用および変更できますが、サポートは制限されており、安定性要件が低く、技術的な能力が強いアプリケーションに適しています。 Enterprise Editionは、安定した信頼性の高い高性能データベースを必要とするアプリケーションに対する包括的な商業サポートを提供し、サポートの支払いを喜んでいます。バージョンを選択する際に考慮される要因には、アプリケーションの重要性、予算編成、技術スキルが含まれます。完璧なオプションはなく、最も適切なオプションのみであり、特定の状況に応じて慎重に選択する必要があります。

インストール後にMySQLの使用方法 インストール後にMySQLの使用方法 Apr 08, 2025 am 11:48 AM

この記事では、MySQLデータベースの操作を紹介します。まず、MySQLWorkBenchやコマンドラインクライアントなど、MySQLクライアントをインストールする必要があります。 1. mysql-uroot-pコマンドを使用してサーバーに接続し、ルートアカウントパスワードでログインします。 2。CreatedAtaBaseを使用してデータベースを作成し、データベースを選択します。 3. createTableを使用してテーブルを作成し、フィールドとデータ型を定義します。 4. INSERTINTOを使用してデータを挿入し、データをクエリし、更新することでデータを更新し、削除してデータを削除します。これらの手順を習得することによってのみ、一般的な問題に対処することを学び、データベースのパフォーマンスを最適化することでMySQLを効率的に使用できます。

高負荷アプリケーションのMySQLパフォーマンスを最適化する方法は? 高負荷アプリケーションのMySQLパフォーマンスを最適化する方法は? Apr 08, 2025 pm 06:03 PM

MySQLデータベースパフォーマンス最適化ガイドリソース集約型アプリケーションでは、MySQLデータベースが重要な役割を果たし、大規模なトランザクションの管理を担当しています。ただし、アプリケーションのスケールが拡大すると、データベースパフォーマンスのボトルネックが制約になることがよくあります。この記事では、一連の効果的なMySQLパフォーマンス最適化戦略を検討して、アプリケーションが高負荷の下で効率的で応答性の高いままであることを保証します。実際のケースを組み合わせて、インデックス作成、クエリ最適化、データベース設計、キャッシュなどの詳細な主要なテクノロジーを説明します。 1.データベースアーキテクチャの設計と最適化されたデータベースアーキテクチャは、MySQLパフォーマンスの最適化の基礎です。いくつかのコア原則は次のとおりです。適切なデータ型を選択し、ニーズを満たす最小のデータ型を選択すると、ストレージスペースを節約するだけでなく、データ処理速度を向上させることもできます。

MySQLインストール後にデータベースのパフォーマンスを最適化する方法 MySQLインストール後にデータベースのパフォーマンスを最適化する方法 Apr 08, 2025 am 11:36 AM

MySQLパフォーマンスの最適化は、インストール構成、インデックス作成、クエリの最適化、監視、チューニングの3つの側面から開始する必要があります。 1。インストール後、INNODB_BUFFER_POOL_SIZEパラメーターやclose query_cache_sizeなど、サーバーの構成に従ってmy.cnfファイルを調整する必要があります。 2。過度のインデックスを回避するための適切なインデックスを作成し、説明コマンドを使用して実行計画を分析するなど、クエリステートメントを最適化します。 3. MySQL独自の監視ツール(ShowProcessList、ShowStatus)を使用して、データベースの健康を監視し、定期的にデータベースをバックアップして整理します。これらの手順を継続的に最適化することによってのみ、MySQLデータベースのパフォーマンスを改善できます。

mysqlはインターネットが必要ですか? mysqlはインターネットが必要ですか? Apr 08, 2025 pm 02:18 PM

MySQLは、基本的なデータストレージと管理のためにネットワーク接続なしで実行できます。ただし、他のシステムとのやり取り、リモートアクセス、または複製やクラスタリングなどの高度な機能を使用するには、ネットワーク接続が必要です。さらに、セキュリティ対策(ファイアウォールなど)、パフォーマンスの最適化(適切なネットワーク接続を選択)、およびデータバックアップは、インターネットに接続するために重要です。

MongoDBデータベースパスワードを表示するNAVICATの方法 MongoDBデータベースパスワードを表示するNAVICATの方法 Apr 08, 2025 pm 09:39 PM

Hash値として保存されているため、Navicatを介してMongoDBパスワードを直接表示することは不可能です。紛失したパスワードを取得する方法:1。パスワードのリセット。 2。構成ファイルを確認します(ハッシュ値が含まれる場合があります)。 3.コードを確認します(パスワードをハードコードできます)。

hadidb:pythonの軽量で水平方向にスケーラブルなデータベース hadidb:pythonの軽量で水平方向にスケーラブルなデータベース Apr 08, 2025 pm 06:12 PM

hadidb:軽量で高レベルのスケーラブルなPythonデータベースHadIDB(HadIDB)は、Pythonで記述された軽量データベースで、スケーラビリティが高くなっています。 PIPインストールを使用してHADIDBをインストールする:PIPINSTALLHADIDBユーザー管理CREATEユーザー:CREATEUSER()メソッド新しいユーザーを作成します。 Authentication()メソッドは、ユーザーのIDを認証します。 fromhadidb.operationimportuseruser_obj = user( "admin"、 "admin")user_obj。

MySQLワークベンチはMariadBに接続できますか MySQLワークベンチはMariadBに接続できますか Apr 08, 2025 pm 02:33 PM

MySQLワークベンチは、構成が正しい場合、MariadBに接続できます。最初にコネクタタイプとして「mariadb」を選択します。接続構成では、ホスト、ポート、ユーザー、パスワード、およびデータベースを正しく設定します。接続をテストするときは、ユーザー名とパスワードが正しいかどうか、ポート番号が正しいかどうか、ファイアウォールが接続を許可するかどうか、データベースが存在するかどうか、MariadBサービスが開始されていることを確認してください。高度な使用法では、接続プーリングテクノロジーを使用してパフォーマンスを最適化します。一般的なエラーには、不十分な権限、ネットワーク接続の問題などが含まれます。エラーをデバッグするときは、エラー情報を慎重に分析し、デバッグツールを使用します。ネットワーク構成を最適化すると、パフォーマンスが向上する可能性があります

See all articles