# objects.delete doesn't remove completely

I have this part of code in my script:

for i in bpy.data.objects:
if ((i.type == "CAMERA") or (i.type == "LAMP") or (i.name == "Material_Holder")):
i.select = False
bpy.ops.object.delete()


when I run this code, all objects except the specified in the if-statment are deleted in the scene. Later in the script I have this part:

for material in bpy.data.materials:
if (not (material.users)):
bpy.data.materials.remove(material)


which checks, if material is assigned to anything and if not, it deletes it. This part does esentiallt nothing. My code creates hundreds of objects and dozens of materials for them. Objects are deleted by the first part, materials are not deleted by the second part. I tried printing material.users and it seems to me, that the objects are still somwhere and have their materials assigned. How can I prevent this?
The whole code:
import bpy

matrix_size = 3

(...)

materials = []
matrix_range = range (matrix_size)

changeable_material = bpy.data.materials.get("Material")

material_value = 0

for i in range (matrix_size):
(...)
materials.append (changeable_material.copy ())
materials [-1].node_tree.nodes.get("Value").outputs [0].default_value = material_value
material_value += 1 / matrix_size
for j in range (matrix_size):
matrix [-1].append ((i * j) % matrix_size)
cubes [-1].append (0)

bpy.ops.object.select_all(action = "SELECT")

for i in bpy.data.objects:
if ((i.type == "CAMERA") or (i.type == "LAMP") or (i.name == "Material_Holder")):
i.select = False
bpy.ops.object.delete()

#removes all unused materials
for material in bpy.data.materials:
if (not (material.users)):
bpy.data.materials.remove(material)

base_object = bpy.context.object
base_object.name = "base cube"
base_object.data.materials.append (changeable_material)

base_object.scale = (0.5, 0.5, 0.5)

bpy.context.scene.objects.active = base_object

arrayOne = base_object.modifiers.new (name = "arrayOne", type = "ARRAY")
arrayOne.count = matrix_size
arrayTwo = base_object.modifiers.new (name = "arrayTwo", type = "ARRAY")
arrayTwo.count = matrix_size
arrayTwo.relative_offset_displace [0] = 0
arrayTwo.relative_offset_displace [1] = 1

bpy.ops.object.modifier_apply (modifier = "arrayOne")
bpy.ops.object.modifier_apply (modifier = "arrayTwo")

base_object.select = True
bpy.ops.mesh.separate (type = "LOOSE")

for i in bpy.data.objects:
if ("cube" in i.name):
bpy.ops.object.select_all(action = "DESELECT")
i.select = True
bpy.ops.object.origin_set (type = "ORIGIN_CENTER_OF_MASS")
cubes [int (i.location [0])][int (i.location [1])] = i

for i in range (matrix_size):
for j in range (matrix_size):
if cubes [i][j].data.materials:
cubes [i][j].data.materials[0] = materials [matrix [i][j]]


except I want to run it for much higher values of matrix_size (this makes matrix_size number of materials and matrix_size * matrix_size number of objects)

• For removing objects and materials As mentioned in answer operator performance is going to get horrendous as you increase number. Perhaps write a new question re the array part as IMO could be done much quicker with object copy and distribute, and object linked material. Oct 18, 2018 at 11:12

Selecting the objects and letting bpy.ops.object.delete() remove them may not be a good idea, not least due to the performance of the bpy.ops calls. Maybe you can get the list of the selected objects using bpy.context.selected_objects. Then iterate over the objects and call:
for obj in bpy.context.selected_objects: