3

私のPython Utilities Github リポジトリには、文字列、マッピング、およびシーケンスから印刷されない文字と無効な Unicode バイトを削除する関数があります。

def filterCharacters(s):
    """
    Strip non printable characters

    @type s dict|list|tuple|bytes|string
    @param s Object to remove non-printable characters from

    @rtype dict|list|tuple|bytes|string
    @return An object that corresponds with the original object, nonprintable characters removed.
    """

    validCategories = (
        'Lu', 'Ll', 'Lt', 'LC', 'Lm', 'Lo', 'L', 'Mn', 'Mc', 'Me', 'M', 'Nd', 'Nl', 'No', 'N', 'Pc',
        'Pd', 'Ps', 'Pe', 'Pi', 'Pf', 'Po', 'P', 'Sm', 'Sc', 'Sk', 'So', 'S', 'Zs', 'Zl', 'Zp', 'Z'
    )
    convertToBytes = False

    if isinstance(s, dict):
        new = {}
        for k,v in s.items(): # This is the offending line
            new[k] = filterCharacters(v)
        return new

    if isinstance(s, list):
        new = []
        for item in s:
            new.append(filterCharacters(item))
        return new

    if isinstance(s, tuple):
        new = []
        for item in s:
            new.append(filterCharacters(item))
        return tuple(new)

    if isinstance(s, bytes):
        s = s.decode('utf-8')
        convertToBytes = True

    if isinstance(s, str):
        s = ''.join(c for c in s if unicodedata.category(c) in validCategories)
        if convertToBytes:
            s = s.encode('utf-8')
        return s

    else:
        return None

時々、この関数は例外をスローします:

Traceback (most recent call last):
  File "./util.py", line 56, in filterCharacters
    for k,v in s.items():
RuntimeError: dictionary changed size during iteration

引数として送信された辞書をどこで変更しているのかわかりません。では、なぜこの例外がスローされるのでしょうか。

ありがとう!

4

1 に答える 1

3

Python 3 では、オブジェクトをdict.items()返しdict_viewます ( listPython 2 とは異なります)。CPython コードを調べてみると、次のようなコメントに気付きました

Objects/dictobject.c

dict_items(register PyDictObject *mp) 
{
    ...
    /* Preallocate the list of tuples, to avoid allocations during
     * the loop over the items, which could trigger GC, which
     * could resize the dict. :-(
     */
    ...

    if (n != mp->ma_used) {
        /* Durnit.  The allocations caused the dict to resize.
         * Just start over, this shouldn't normally happen.
         */
        Py_DECREF(v);
        goto again;
    }
    ...
}

したがって、辞書の削除と挿入だけでなく、割り当てもこのエラーを表示する可能性があります! oO!

リサイズの手順も面白い。見る

static int
dictresize(PyDictObject *mp, Py_ssize_t minused)
{
    ...
}

しかし、それはすべて内部です。

解決

dict_viewで変換しlistてみる

if isinstance(s, dict):
    new = {}
    items = [i for i in s.items()]
    for k,v in items:
        new[k] = filterCharacters(v)
    return new
于 2013-07-19T18:55:48.430 に答える