Yes because arrow.copy()
only copies the object data-block but not the mesh data-block that resides inside arrow.data
. And since the material is by default linked to the mesh, you will end up with an object containing the same mesh and hence the same material. You can change that by either: (1) duplicating the mesh new_arrow.data = arrow.data.copy()
with this script:
import bpy
C = bpy.context
D = bpy.data
arrow = D.objects['cyl']
new_arrow = arrow.copy()
new_arrow.data = arrow.data.copy()
new_arrow.location = (0,0,2)
mat = bpy.data.materials.new("PKHG")
mat.diffuse_color = (1,0,0,1)
new_arrow.active_material = mat;
C.collection.objects.link(new_arrow)
new_arrow2 = arrow.copy()
new_arrow2.data = arrow.data.copy()
new_arrow2.location = (0,2,2)
mat = bpy.data.materials.new("PKHG")
mat.diffuse_color = (1,1,0,1)
new_arrow2.active_material = mat;
C.collection.objects.link(new_arrow2)
or (2) if you want to have copied objects linked to the same mesh, you can change the behavior of the object to have materials linked to Object instead of Data using new_arrow.material_slots[0].link = 'OBJECT'

by using this script:
import bpy
C = bpy.context
D = bpy.data
arrow = D.objects['cyl']
new_arrow = arrow.copy()
new_arrow.material_slots[0].link = 'OBJECT'
new_arrow.location = (0,0,2)
mat = bpy.data.materials.new("PKHG")
mat.diffuse_color = (1,0,0,1)
new_arrow.active_material = mat;
C.collection.objects.link(new_arrow)
new_arrow2 = arrow.copy()
new_arrow2.material_slots[0].link = 'OBJECT'
new_arrow2.location = (0,2,2)
mat = bpy.data.materials.new("PKHG")
mat.diffuse_color = (1,1,0,1)
new_arrow2.active_material = mat;
C.collection.objects.link(new_arrow2)