Node2D and Node3D classes implemented using a shared decorator
One thing that I don't like, and doesn't seem to make sense, is that I had to move the list of valid keys from the validator to the parent class itself 


That's not only something to dislike -- it is actually broken. If you do this...

n3 = Node3D()
n2 = Node2D()
n3.coords = {'x':1,'y':2,'z':3}


...you will get Exception because the second line has caused Node.valid_keys to be overwritten. To fix that, you could eliminate all three __new__ methods and instead define the subclasses like this:

class Node3D(Node):
    valid_keys ='x','y','z'

class Node2D(Node):
    valid_keys ='x','y'


However, the whole approach is not very good because one can still do for example this:

&gt;&gt;&gt; node = Node2D()
&gt;&gt;&gt; node.coords = {'y': 2, 'x': 1}
&gt;&gt;&gt; node.z = 3
&gt;&gt;&gt; node.coords
{'y': 2, 'x': 1, 'z': 3} 


Using a namedtuple as discussed in comments is a better idea.