enumerate returns 2 items (per iteration) - the index (0 through n) and the elements themselves (ordered).
So this the case you give:
for row, tiles in enumerate(self.map_data):
    for col, tile in enumerate(tiles):
        if tile == "1":
        Wall(self, col, row)
map_data is a 2D array of tiles that has rows and columns.
In the first enumerate you get the row number (called row here) and the row itself (which is a 1D array of tiles called tiles here). Then the second enumerate goes over this 1D array of tiles (called tiles) and gets the index (which is the columns) and the element itself, which is the tile (called tile).
You could conceptually rewrite this like so which might be more clear:
for i, map_data[i] in enumerate(map_data):
    for j, map_data[i,j] in enumerate(map_data[i]):
        tile = map_data[i,j]
        if tile == "1":
        Wall(self, col, row)
I don't know the context, but do notice that the ==1 relates to the element itself, not the index, so in this case tile is probably coded such that 1 denotes the presence of a wall.
edit: I noticed in the original code you use self. but I assume it's a typo, and I wrote self, here.
edit 2: I hope this example will help, using a list of strings, which appears to be the data you have.
let's assume you have
map_data = ['1111','1001','1101','1111']
the first loop will give 0,1,2,3 for row and then '1111', 1001','1101','1111' for tiles. Then the second loop will give 0,1,2,3 for col and '1','1','1','1' for tile on the first row (where row is 0 and tiles is '1111'), 0,1,2,3 and '1','0','0','1' for the second row ('1001') etc.