Something along these lines may work. This version assumes that your lamp-posts are instances, and the only transforms responsible for putting them where they are, are their own, without the interference of parenting or constraints. You may need to arrange that, by, for instance, clearing their parenting, keeping their transforms.
- Make a backup of your file
- Alt D create an instance of one of your lamp-posts
- (Probably, you'll want to isolate it / hide everything else, so you can see what you're doing)
- Ctrl A, or via the Header > Object menu, > Clear all its transforms
- Position your light where you would like it, relative to the lamp-post
- M Add the light and the lamp-post to a new collection, called, say, 'Practical'
- With Shift C the 3D cursor at World 0, Shift A add a new Collection Instance to the scene, based on the new collection.
- Unhide your original lamp-posts, select them, and then your
'Practical' Collection Instance, so it is the active object.
Now we want to make a copy of the Collection Instance in the place of all the existing lamp-posts. I'm surprised there isn't an operator / add-on to do this without scripting, but unless someone finds one for us, here goes.. run this script:
C = bpy.context
D = bpy.data
src = C.active_object
sel = C.selected_objects
cp_col = D.collections.new('Practicals')
for ob in sel:
cp = src.copy()
cp.matrix_world = ob.matrix_world
it should create the copies in place, and put them in a new collection called 'Practicals'. (plural)
Hopefully, you should now be able to hide the original lamp-posts, and see them replaced with practical, light-bearing instances. And be left in a state from which you can change all the lights / do whatever you like, by editing the objects in the 'Practical' collection from which the Collection Instance is derived.