Python 不将更新返回到YAML文件,并在字符串更新时返回TypeError

Python 不将更新返回到YAML文件,并在字符串更新时返回TypeError,python,python-3.x,dictionary,pyyaml,Python,Python 3.x,Dictionary,Pyyaml,我有一段代码,应该可以抓取一个YAML文件并将其读入(确实成功),但也应该能够更新YAML,并返回三个选项中的一个: 成功更新消息,包含对文件的所有更改 不成功的更新消息,其中可能有文件未正确更新的位置 让用户知道他/她没有传递对文件所做的任何更改(YAML文件中的冗余条目) 第二个和第三个选项运行良好,但第一个选项返回TypeError:每当我更新字典d中嵌套字典之外的任何值时,字符串索引必须是整数或TypeError:“int”对象不可编辑 下面是我想出的代码: class YAML_C

我有一段代码,应该可以抓取一个YAML文件并将其读入(确实成功),但也应该能够更新YAML,并返回三个选项中的一个:

  • 成功更新消息,包含对文件的所有更改
  • 不成功的更新消息,其中可能有文件未正确更新的位置
  • 让用户知道他/她没有传递对文件所做的任何更改(YAML文件中的冗余条目)
第二个和第三个选项运行良好,但第一个选项返回
TypeError:每当我更新字典d中嵌套字典之外的任何值时,字符串索引必须是整数
TypeError:“int”对象不可编辑

下面是我想出的代码:

class YAML_Config:
    '''
    Class used to interact with .YAML filetypes. Allows the creation of objects \
    that can be manipulated and save certain YAML files and configuration settings.
    '''

    def __init__(self, filename):
        '''
        Initial defintion. Defines the location for saving a new .YAML file or for loading \
        a previous one. The dictionary corresponding to the .YAML file is saved to self.dict. \
        If this is a new .YAML file, an empty dictionary is created.

        Input Arguments:
            -filename: (string) The name of the file where the current .YAML file is \
            located or the new .YAML will be saved.
        '''
        #Get the filename and save it to the class as a property.
        self.file = filename

        #Check if the file exists...
        if os.path.isfile(self.file):

            #Prepare to open the file with reading capabilities
            with open(self.file,'r') as infile:

                #Get a dictionary with all of the YAML informatin.
                yaml_dict = yaml.load(infile)

        #If the file does not exist...
        else:

            #Create an empty dictionary to save all YAML data.
            yaml_dict = {}

            #Create an empty .yaml file with the dictionary.
            with open(self.file, 'w') as infile:

                #Save updated dictionary to YAML file.
                yaml.dump(yaml_dict, infile, default_flow_style=False)

            print('YAML configuration file not found. New, empty dictionary, and .yaml file created.')

        self.dict=yaml_dict




    def update_value(self, kwargs):
        '''
        Used to update YAML files, and checks that the files were updated properly.
        '''
        #If these are not keyword arguments, then throw an error.
        assert kwargs, 'Input Error'

        #Get the YAML dictionary before it is updated.
        yaml_dict = self.dict


        #Make a copy of the dictionary to compare against later.
        yaml_dict_original = copy.deepcopy(self.dict)

        #Check if the dictionary is nonetype. This happens if the file is new as the file is empty.
        if yaml_dict_original is None:

            #Redefine orginal dictionary as an empty dictionary.
            yaml_dict_original={}

            #The new dictionary will simply be what is passed to the function.
            yaml_dict=kwargs

        else:

            #Update the original dictionary and update it with the arguments passed.
            #This also updates self.dict as yaml_dict is simply a reference to
            #that dictionary, not a copy of it.
            yaml_dict.update(kwargs)


        #Check if changes were made
        if (yaml_dict==yaml_dict_original) is False:

            #Open the YAML file to write to it.
            with open(self.file, 'w') as outfile:

                #Save updated dictionary to YAML file.
                yaml.dump(self.dict, outfile, default_flow_style=False)


    #Check that the file actually updated properly:

            #Double-check the file that it actually updated.
            with open(self.file, 'r') as infile:

                lastupdate = yaml.load(infile)

            #Get any nonmatching values between what should be in the YAML file and what actually is.
            errors = { k : yaml_dict[k] for k in set(yaml_dict) - set(lastupdate) }

            #Find out what changed in the YAML file.
            edits = { k : yaml_dict_original[k] for k in set(yaml_dict_original) - set(lastupdate) }

            #Check if errors is not empty. Evaluating dictionaries as boolean either returns True (not empty)
            #or False (empty).
            if bool(errors) is True:

                #First line of return print statement.
                print('The following entries did not update successfully:')

                #Loop through keys in errors
                for n in errors:

                    #Print the current key
                    print (n)

                    #Loop through entries of current key
                    for m in errors[n]:

                        #Print current entry of current key
                        print (m,':',errors[n][m])

            #Saved properly, check for edits and display to user.
            else:

                #Find where any edits were made.
                edits = {k: yaml_dict_original[k] for k in yaml_dict_original if k in lastupdate and yaml_dict_original[k] != lastupdate[k]}

                #Show user what edits were successfuly made.
                print('%s was successfully updated with the following changes:' %  os.path.basename(self.file))

                #Loop through keys in edits
                for n in edits:

                    #Print the current key
                    print (n)

                    #Loop through entries of current key
                    for m in edits[n]:

                        #Print current entry of current key
                        print (m,':',edits[n][m])

        #If no changes were made...        
        else:

            #Show user what edits were successfuly made.
            print('No changes to %s were passed. File not updated.' %  

    os.path.basename(self.file))



test=YAML_Config(r'...Path\Python Work\yaml_test.yaml')

d = {'A': 7, 'B':{'C':'D', 'D':False, 'E':'Julio'},\
     'The Real C': {'J?':'Yes, this is J.', 'K' : 241},'Q' : 'PQ'}

test.update_value(d)
第一个错误部分:

b = {'A': '', 'B':{'C':'D', 'D':False, 'E':'Julio'},\
         'The Real C': {'J?':'Yes, this is J.', 'K' : 241},'Q' : 'PQ'}

#TypeError: string indices must be integers.
test.update_value(b)
f = {'A': 7, 'B':{'C':'D', 'D':False, 'E':'Julio'},\
         'The Real C': {'J?':'Yes, this is J.', 'K' : 241},'Q' : 2}

#TypeError: 'int' object is not iterable.
test.update_value(f)
第二个错误部分:

b = {'A': '', 'B':{'C':'D', 'D':False, 'E':'Julio'},\
         'The Real C': {'J?':'Yes, this is J.', 'K' : 241},'Q' : 'PQ'}

#TypeError: string indices must be integers.
test.update_value(b)
f = {'A': 7, 'B':{'C':'D', 'D':False, 'E':'Julio'},\
         'The Real C': {'J?':'Yes, this is J.', 'K' : 241},'Q' : 2}

#TypeError: 'int' object is not iterable.
test.update_value(f)
每次运行此代码时,YAML文件都会更新。因此,实际的更新是有效的,但我不确定为什么我找不到字典更新的索引。我对Python有点生疏,对YAML还不熟悉,所以我可能在这里遗漏了一些明显的东西


我使用的是Python 3.6.5。

您的代码在这一行出错:

                    # Loop through entries of current key
                    for m in edits[n]:
此时,
edits
是一个dict:
{a:7}
(因为a是具有更改值的键)。您的代码似乎只预期复杂值的变化(如
B
),因为如果您这样做:

b = {'A': 7, 'B':{'C':'D', 'D':True, 'E':'Julio'},\
        'The Real C': {'J?':'Yes, this is J.', 'K' : 241},'Q' : 'PQ'}
你会得到:

test.yaml was successfully updated with the following changes:
B
C : D
D : False
E : Julio
因此,您应该重新考虑您的“报告”,并将其更新为不仅可以处理
dict
s的值,还可以处理
list
s和原语(YAML标量:string、int、boolean等):

然后您的输出将是:

test.yaml was successfully updated with the following changes:
A: 7
test.yaml was successfully updated with the following changes:
A: 
Q: PQ

(假设两个额外的更新都在后面运行)。

额外的换行符会迫使读卡器执行更多的滚动操作,而其他非格式化操作也不会使代码更具可读性。我想如果您使用的是PyYAML,您可能不关心输入文件中的注释,也不关心YAML 1.2的一致性,或者是吗?请注意,由于PyYAML 4.1是从PyPI中收回的,因此再次使用
YAML.load()
更加危险(而且没有必要)
您应该养成始终显式使用
YAML.safe\u load()的习惯
使用PyYAML时。请更新程序,以便它抛出您得到的错误。嘿,Anthon,感谢您提供一些PEP8格式。我不太关心
yaml.load()
vs
yaml.safe\u load()
,因为我将使用的所有.yaml文件都是由该程序创建的。从我对两者之间的区别的理解来看,这是能够执行嵌入yaml文件中的代码的区别,这是我永远不会想到的,因为没有其他用户会与这些文件交互。我不太确定您对输入文件中的注释是什么意思,但我使用的是PyYAML 3.12。如果您是指.YAML文件中的行内注释,那么您是正确的。我真的不在乎他们,因为这些文件应该已经是不言自明的,我将制作的真正文件的键名是什么。我更新了代码,在执行相同的操作时,它返回了相同的错误:
TypeError:“int”对象不可编辑
TypeError:字符串索引必须是整数