【问题标题】:python: Error: dictionary changed size during iteration @ when trying to iterate over django request object at SimpleLazyObjectpython:错误:迭代期间字典更改大小@尝试在 SimpleLazyObject 处迭代 django 请求对象时
【发布时间】:2020-01-09 11:01:04
【问题描述】:

在字典中的python中,如果keybyte string,那么json.dumps会抛出错误,所以我试图将所有keys递归转换为string,然后再将它们传递给json.dumps

Note: json.dumps converts the value to str using default function but not keys

以下是我的函数,它将检查任何byte string keys 并将它们转换为string

def keys_string(d):
    rval = {}
    if not isinstance(d, dict):
        if isinstance(d,(tuple,list,set)):
            v = [keys_string(x) for x in d]
            return v
        else:
            return d
    for k,v in d.items():
        if isinstance(k,bytes):
            k = k.decode()
        if isinstance(v,dict):
            v = keys_string(v)
        elif isinstance(v,(tuple,list,set)):
            v = [keys_string(x) for x in v]
        rval[k] = v
    return rval

我在 django 中调试一些代码

我想在我的代码的某个位置检查 request 对象

所以我有

request_dir = dir(request)

然后使用 keys_string 将任何字节键转换为字符串(否则 json 转储将引发错误)

request_dir_keys_stringed = keys_string(request_dir)

然后终于

json.dumps(request_dir_keys_stringed, indent=4, sort_keys=True, default=str)

当我尝试做 request_dir_keys_stringed = keys_string(request_dir) 它说

in keys_string
    for k,v in d.items():
RuntimeError: dictionary changed size during iteration

我发现这种情况发生在:

k: userv: <SimpleLazyObject: <User: test@gmail.com>>

我尝试了request.session 对象,它不会抛出这样的错误。但有些对象会。

request_session_dir = dir(request.session)
request_session_dir_keys_stringed = keys_string(request_session_dir)
json.dumps(request_session_dir_keys_stringed, indent=4, sort_keys=True, default=str)

遇到这种情况怎么办

重现问题的更多信息:

$ python --version
Python 3.7.3

$ django-admin --version
2.2.6

def articles(request):
    request_dir = dir(request)
    request_dir_keys_stringed = keys_string(request_dir)
    print(json.dumps(request_dir_keys_stringed, indent=4, sort_keys=True, default=str)
    return render(request, 'articles/main_page/articles.html')

实施解决方案后,keys_string 变为:

def keys_string(d):
    rval = {}
    if not isinstance(d, dict):
        if isinstance(d,(tuple,list,set)):
            v = [keys_string(x) for x in d]
            return v
        else:
            return d
    keys = list(d.keys())
    for k in keys:
        v = d[k]
        if isinstance(k,bytes):
            k = k.decode()
        if isinstance(v,dict):
            v = keys_string(v)
        elif isinstance(v,(tuple,list,set)):
            v = [keys_string(x) for x in v]
        rval[k] = v
    return rval


        request_dir = dir(request)
        request_dir_keys_stringed = keys_string(request_dir)
        print(json.dumps(request_dir_keys_stringed, indent=4, sort_keys=True, default=str)

现在请求对象显示没有任何错误

【问题讨论】:

  • Python 和 Django 版本好吗? (无法在此处重现该问题)。
  • 我已经在编辑的问题中添加了信息
  • dir(request) 不总是返回 list 吗?所以你的代码永远不会到达for 循环?
  • 有人真的可以用 OP 的代码重现这个问题吗? dir() 返回一个字符串列表,所以我看不到它如何进入for k,v in d.items() 部分。 @SanthoshYedidi 您发布的代码不是实际引发此错误的代码,或者内置的 dir 函数此时被其他东西所掩盖。请在函数的开头添加print(dir) 并发布它打印的内容。
  • 是的,可以是列表,但是列表可以是对象的集合。像 [ 'someitem,[{},{}],{},etc] 所以第一次它可能不会进入循环,但当更深入时它应该

标签: python django dictionary


【解决方案1】:

request.userSimpleLazyObject ,它包含一个回调,该回调是一个包含对相同 request 对象的引用的闭包。然后该回调通过创建新的 attr request._cached_user 对象来更新 request 对象(如果它不存在)。所以观察request.user 可能会创建一个新的request._cached_user 属性。

A认为用代码摘录更容易解释。

来自 django 源代码:

class SimpleLazyObject(LazyObject):
    def __init__(self, func):
        ...

class AuthenticationMiddleware(MiddlewareMixin):
    def process_request(self, request):
        request.user = SimpleLazyObject(lambda: get_user(request))

def get_user(request):
    if not hasattr(request, '_cached_user'):
        request._cached_user = auth.get_user(request)
    return request._cached_user

因此,如果您想更稳定地遍历字典键,那么您需要遍历字典的键副本:

keys = list(d.keys())
for k in keys:
    v = d[k]
    if isinstance(k, bytes):
        ...

【讨论】:

  • dir() 返回一个字符串列表... OP 的函数如何从字符串列表中访问 request.user ? (nb:并不意味着你走错了路——我实际上怀疑操作的 dir() 不是内置的——只是考虑到操作的 sn-p 整个事情没有意义)。跨度>
  • 我在一些答案中读到 d.items() 也会复制密钥。所以那不是真的。这非常棒。它拯救了我的一天。谢谢@petraszd。
  • dir() 返回一个字符串列表 是的。但是,如果您将 list 传递给 keys_string 而不是在第 5 行,它将为每个项目调用 keys_string。其中一项将是HttpRequest 的实例的__dict__
猜你喜欢
  • 2019-12-02
  • 1970-01-01
  • 2021-11-21
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-15
相关资源
最近更新 更多