Simple code to modify material of an object
You can:

import bpy

#Get the object
obj = bpy.data.objects['Cube']
#Get the material
mat = bpy.data.materials['Material']

#Check all is ok
if obj and mat:

    #If a material is already assigned (material slots is not empty)
    if obj.material_slots:
        #Assign the material to the first slot
        obj.material_slots[0].material = mat
    #If no material is assigned
    else:
        #Create a new material (link/slot) for the object
        obj.data.materials.append(mat)


Explanations:

Materials are stored at two levels: object and data

'data' contains an array of materials (obj.data.materials) that allows to link the material to the object's data.

'obj' contains an array of material slots (obj.material_slots) that contains slots which give a link to the material itself (obj.material_slots[0].material) and other properties (name and link). This material is not necessarily the same as the corresponding obj.data.material (see 'link property' below).

These two lists are internally maintained coherently.

The 'link' property of the material's slot can be either 'OBJECT' or 'DATA'.
That corresponds to this is the UI:



This distinction is useful when you 'duplicate linked' AltD an object. In this case:


If the link is 'OBJECT': each duplicate can have its own material in the material slot
If the link is 'DATA': all duplicates will share the same material


So, if we come back to the code:

#If a material is already assigned (material slots is not empty)
if obj.material_slots:
    #Assign the material to the first slot
    obj.material_slots[0].material = mat


In the first case above, using a previously created slot will assign the material coherently to the link property.

#If no material is assigned
else:
    #Create a new material (link/slot) for the object
    obj.data.materials.append(mat)


In this second case, we assign a new material which will correspond to a new slot and a default 'DATA' link mode.