This seems to depend on whether you are using the built-in ElementTree, or lxml.
With lxml, you should be able to use copy:
from lxml import etree
e = etree.Element('root')
etree.SubElement(e, 'child1')
etree.SubElement(e, 'child2')
from copy import copy
f = copy(e)
f[0].tag = 'newchild1'
etree.dump(e)
<root>
<child1/>
<child2/>
</root>
etree.dump(f)
<root>
<newchild1/>
<child2/>
</root>
You can see that the new tree is actually separate from the old one; this is because lxml stores the parent in the element, and so can't reuse them - it has to create new elements for every child.
ElementTree doesn't keep the parent in the element, and so it's possible for the same element to coexist in several trees at once. As far as I can tell, there's no built-in way to force deep copying... deepcopy and element.copy() both do the exact same thing as copy - they copy the node, but then connect it to the children from the original node. So changes to the copy will change the original - not what you want.
The simplest way I've discovered to make this work properly is simply to serialize to a string, and then deserialize it again. This forces completely new elements to be created. It is pretty slow - but it also always works. Compare the following methods:
import xml.etree.ElementTree as etree
e = Element('root')
etree.SubElement(e, 'child1')
etree.SubElement(e, 'child2')
#f = copy(e)
#f[0].tag = 'newchild1'
# If you do the above, the first child of e will also be 'newchild1'
# So that won't work.
# Simple method, slow but complete
In [194]: %timeit f = etree.fromstring(etree.tostring(e))
10000 loops, best of 3: 71.8 µs per loop
# Faster method, but you must implement the recursion - this only
# looks at a single level.
In [195]: %%timeit
.....: f = etree.Element('newparent')
.....: f.extend([x.copy() for x in e])
.....:
100000 loops, best of 3: 9.49 µs per loop
This bottom method does create copies of the first-level children, and it is a lot faster than the first version. However, this only works for a single level of nesting; if any of these had children, you'd have to go down and copy those yourself as well. You may be able to write a recursive copy, and it might be faster; the places where I've done this haven't been performance-sensitive so I haven't bothered in my code. The tostring/fromstring routine is fairly inefficient, but straightforward, and always works no matter how deep the tree is.