Three very similar functions operating on XML files I have 3 functions which are annoyingly similar, but I can't figure out a way to refactor them without making the code unreadable.

The three functions deal with xml files (.resx), the first function reads all files in a directory and "collects" all strings inside a data.value tag, adds them to a set and returns that set, for example:

&lt;data name="AccessibilityTitleResource1.Text" xml:space="preserve"&gt;
&lt;value&gt;Pages Accessibility&lt;/value&gt;


The second function reads these same files, and swaps the values read previously with a translation which is inside a dict called definicoes (definicoes[english] = translation), with the previous example, then saves the file. So from our previous example definicoes["Pages Accessibility"] = "Acessibilidade de Páginas". The xml file would become:

&lt;data name="AccessibilityTitleResource1.Text" xml:space="preserve"&gt;
&lt;value&gt;Acessibilidade de Páginas&lt;/value&gt;


The third function simply checks if the xml file is fully translatable, so it checks all the values &lt;value&gt; tags inside the &lt;data&gt; ones against the ones in the dict, if there is any value not present on the dict it returns False.

Here are the three functions:

def ler_todos(caminho) -&gt; set:
    definicoes = set()
    for file in os.listdir(caminho):
        if file.endswith(".ascx.resx") or file.endswith(".aspx.resx"):
            tree = ET.parse(caminho + file)
            root = tree.getroot()
            for child in root.iter('data'):
                if child.attrib.get('namespace') == 'preserve':
                    node = child[0]
                    if node.tag == "value" and node.text != None and not node.text in definicoes:
                        definicoes.add(node.text)
    return definicoes


def escrever_arquivo(definicoes, caminho):
    if caminho.endswith(".pt.resx"):
        tree = ET.parse(caminho)
        root = tree.getroot()
        for child in root.iter('data'):
            if child.attrib.get('namespace') == 'preserve':
                node = child[0]
                if node.tag == "value" and node.text != None and node.text in definicoes:
                    node.text = definicoes[node.text]
        tree.write(caminho, encoding="UTF-8")
        anexar_cabecalho(caminho)

def existe_traducao(definicoes, caminho):
    if caminho.endswith(".resx"):
        tree = ET.parse(caminho)
        root = tree.getroot()
        for child in root.iter('data'):
            if child.attrib.get('namespace') == 'preserve':
                node = child[0]
                if node.tag == "value" and node.text != None and len(node.text) &gt; 0 and node.text not in definicoes:
                    return False
        return True
One of the possible ways to improve on following the DRY principle would be to extract the XML tree iteration part which is the same in all 3 functions. Something like this generator:

def values(tree):
    root = tree.getroot()
    for child in root.iter('data'):
        if child.attrib.get('namespace') == 'preserve':
            node = child[0]
            if node.tag == "value" and node.text is not None:
                yield node


Then, you can use it this way:

tree = ET.parse(caminho)
for node in values(tree):
    # do something with node