【问题标题】:strange python 2.7 dict behavior: key not inside while clearly there奇怪的python 2.7 dict行为:钥匙不在里面,虽然很明显
【发布时间】:2013-07-01 21:52:16
【问题描述】:

我编写了一个解析器,它接受 JSON 配置并从中创建对象。我首先创建了一个众所周知的对象,并尝试动态导入一个模块(可能来自用户),同时通过该模块定义的creator 方法加载其类。

这是一些测试代码:

import json
import imp
import os.path as path
from lib.config.members import Member
from lib.tasks.task import Task


class Parser(object):

    def __init__(self):
        self._loadedMods = {"tasks": {}}

    def _load_module(self, clazz, modPart):
        """
        imports and caches a module.

        :param clazz: the filename of the module (i.e email, ping...)
        :param modPart: the folder of the module. (i.e services, parsers...)
        :return: the imported/cached module, or throws an error if it couldn't find it
        """
        mods = self._loadedMods[modPart]
        if clazz in mods:
            return mods["class"]
        else:
            #mod = __import__(clazz)

            p = path.join("lib", modPart, clazz + ".py")
            mod = imp.load_source(clazz, p)
            mods[clazz] = mod
            return mod

    def replace_with_import(self, objList, modPart, items_func, class_check):
        """
        replaces configuration dicts with their objects by importing and creating it in the first step.
        In the second step the original list of json config dicts gets replaced by the loaded objects

        :param objList: the list of objects which is iterated on
        :param modPart: the folder from the module (i.e tasks, parsers)
        :param items_func: function to get a pointer on the list of json-config-objects to replace. Takes one argument and
        should return a list of
        :param class_check: currently unsupported
        """
        for obj in objList:
            repl = []
            items = items_func(obj)
            for clazzItem in items:
                try:

                    clazz = clazzItem["class"]
                    mod = self._load_module(clazz, modPart)
                    item = mod.create(clazzItem)
                    if class_check(item):
                        repl.append(item)
                    else:
                        print " ignoring class " + clazzItem["class"] + "! It does not pass the class check!"

                except ImportError, err:
                    print "could not import " + clazz + ": " + str(clazzItem) + "! reason:"
                    print str(err)
                except KeyError, k:
                    print "Key " + str(k) + " not in classItem " + str(clazzItem)
                except Exception, e:
                    print "Error while replacing class ( " + clazz + " :" + str(e) + ")"

            del items[:]
            items.extend(repl)

    def _create_raw_Object(self, jsonDict, msgName, creator):
        """
        creates an Main object from the configuration, but just parses raw data and hands it to the object

        :param jsonDict: the configuration file part as dict
        :param msgName: name of object for error message
        :param creator: function pointer which is taking two arguments: identifier of the object and arguments.
        :should return an object
        :return: a list of objects returned by creator
        """
        items = []
        for key, val in jsonDict.items():
            try:
                item = creator(key, val)
                items.append(item)
            except Exception, e:
                print "ignoring " + msgName + ": " + key + "! reason:"
                print str(e)
        return items

jsonFile = '''
{
    "members":{
        "homer":{
            "name": "Homer Simpson",
            "comment": "Security Inspector",
            "tasks": [{"class":"email", "type": "donut", "args": {"rcpt": "homer_j_simpson@burnscorp.sp"}},
            {"class":"email", "type": "do", "args": {"rcpt": "my_other_mail@burnscorp.sp"}}]
        }
    }
}
'''

jsonDict = json.loads(jsonFile)

parser = Parser()

creator = lambda name, values: Member(name, **values)
members = parser._create_raw_Object(jsonDict["members"], "Members", creator)

items_func = lambda member: member.get_tasks()
class_check = lambda task: isinstance(task, Task)
parser.replace_with_import(members, "tasks", items_func, class_check)

for d in members:
    print d.__dict__

如您所见,成员可以有一个任意任务列表,它应该导入哪个任务在其class 属性中定义,但只要其中两个具有相同的类值(应该不要按照我们定义的方式破坏 json)我得到一个奇怪的 KeyError

Key 'class' not in classItem {u'args': {u'rcpt': u'my_other_mail@burnscorp.sp'}, u'type': u'do', u'class': u'email'}

为什么会出现这个奇怪的错误?非常欢迎任何提示我给我一个线索的提示,因为我感到绝望,调试了几个小时。

我认为 Member 和 Email/Task 类是不相关的,但为了完整性我会发布它们:

lib/config/members.py

class Member:
    def __init__(self, id, name="",  comment="", tasks=None):
        self.id = id
        self.name = name
        self.tasks = []
        self.add_task(tasks)
        self.comment = comment

    def get_id(self):
        return self.id

    def add_task(self, task):
        if task is None:
            return
        if isinstance(task, list):
            self.tasks.extend(task)
        else:
            self.tasks.append(task)

    def get_tasks(self):
        return self.tasks

lib/tasks/[task|email].py

class Task:
    """
    Base class for all built-in Tasks.
    """

    def set_task_type(self, taskType):
        """
        sets the type of this task.

        Be aware! this method can only get called once!

        :param taskType: the type of this task
        """
        if hasattr(self, "_taskType"):
            raise Exception("taskType is only allowed to set once!")
        self.taskType = taskType  

    def get_task_type(self):
        """
        :return: the type set by set_type_task
        """
        return self._taskType

"""
The email task.
"""

from lib.tasks.task import Task


class EmailTask(Task):
    def __init__(self, **kwargs):
        self.set_task_type(kwargs["type"])
        self.recipient = kwargs["args"]["rcpt"]

    def execute_task(self, msg):
        pass

def create(taskDict):
    return EmailTask(**taskDict)

【问题讨论】:

  • 尝试将密钥转换为unicodekey = key.decode('utf-8')
  • 我会尝试打印出原始异常,而不是您自己的 print。只是为了确保您没有收到与您想象的不同的异常。
  • @Wessie,你带来了启示!!这是 _load_module 的方法,它被扔在 if clazz in mods: return mods["class"] :) 将此作为答案发布,我会接受

标签: python python-2.7 dictionary import keyerror


【解决方案1】:

您似乎正在吃实际的异常,将其替换为您自己在replace_with_import 中的自定义print。正如我在评论部分指出的那样。

您通常希望 try 块保持小且非常可预测,确切地知道在代码中可以提出什么以及应该处理什么。 try 块的复杂性越低越好。

【讨论】:

    猜你喜欢
    • 2017-12-12
    • 2019-04-08
    • 1970-01-01
    • 2021-11-13
    • 1970-01-01
    • 1970-01-01
    • 2011-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多