Never use embedded cursors. For each record in the outer table you will have to read the entire other table, even if only one record in the table matches. This is because cursors are strictly linear and do not allow any random access to the records. This means that two tables of 10,000 records each (20,000 records if each was read just once) will result in the second table reading the equivalent of 100,000,000 records to complete the loop (which is 99,990,000 records too many).
Dictionaries are random access collections with immediate look up of any record based on a key value. Load a dictionary from a search cursor of the table that you want to use as the look up control, and then you will only have to read the second table once to match to any of the unique key values in the dictionary. Multifield keys are just tuples. See my Blog and look at the example of Using a Multi-Field Python Dictionary Key to Replace a Concatenated Join Field.
from time import strftime
print "Start script: " + strftime("%Y-%m-%d %H:%M:%S")
import arcpy
sourceFC = r"C:\Path\SourceFeatureClass"
sourceFieldsList = ["ROAD_NAME", "DISTRICT", "CITY", "ValueField"]
# Use list comprehension to build a dictionary from a da SearchCursor where the key values are based on 3 separate feilds
valueDict = {(r[0:2]):(r[3:]) for r in arcpy.da.SearchCursor(sourceFC, sourceFieldsList)}
updateFC = r"C:\Path\UpdateFeatureClass"
updateFieldsList = ["NAME.ROAD", "DISTRICT", "CITY", "ValueField"]
with arcpy.da.UpdateCursor(updateFC, updateFieldsList) as updateRows:
for updateRow in updateRows:
# store the Join value by combining 3 field values of the row being updated in a keyValue variable
keyValue = (updateRow[0:2])
# Find any keyValue that is not in the Dictionary
if not keyValue in valueDict:
# Key not found in dictionary so do something to unmatched entry
updateRow[3] = "fields unmatched"
updateRows.updateRow(updateRow)
del valueDict
print "Finished script: " +strftime("%Y-%m-%d %H:%M:%S")
==
) and assignment (=
)