Find maximum area of island in matrix I recently solved the problem below from leetcode:


  Given a non-empty 2D array grid of 0's and 1's, an island is a group of 1's (representing land) connected 4-directionally (horizontal or vertical).  Find the maximum area of an island in the given 2D array.


This is how I normally write python code. I am thinking this might not be really Pythonic. Please help review the code.

from operator import add


def max_area_of_island(grid):
    """
    :type grid: List[List[int]]
    :rtype: int
    """
    rlen = len(grid)
    clen = len(grid[0])

    visited = [[0] * clen for _ in range(rlen)]
    max_island = 0
    dirs = [(-1, 0), (1, 0), (0, -1), (0, 1)]

    def add_dir(cur_loc, d):
        cur_i, cur_j = cur_loc
        new_i, new_j = tuple(map(add, cur_loc, d))
        if new_i &gt;= 0 and new_i &lt; rlen and new_j &gt;= 0 and new_j &lt; clen:
            #print("all good")
            return new_i, new_j
        #print("error")
        return -1, -1

    max_area = 0
    for i in range(rlen):
        for j in range(clen):
            if grid[i][j] == 0 or visited[i][j]:
                continue
            area = 1
            q = [(i,j)]
            visited[i][j] = True
            #print("before qsize", q.qsize())
            while q:
                #print("during qsize", q.qsize())
                cur = q.pop()
                for _,d in enumerate(dirs):
                    new_i, new_j = add_dir(cur, d)
                    if new_i &lt; 0 or visited[new_i][new_j]: continue
                    if new_i &gt;= 0 and grid[new_i][new_j]:
                        new_loc = (new_i, new_j)
                        q.append(new_loc)
                        visited[new_i][new_j] = True
                        area += 1
            max_area = max(area, max_area)

    return max_area
Some suggestions:


Nested functions are unusual; typically they would be neighbour functions instead. This ensures that all the context that each function needs is passed to it, making it easier to grasp the entire context involved in the processing in each function.
Python 3 supports type annotations, which are a more explicit way of declaring input and output types. You can check that your code is properly annotated using mypy, for example with this rather strict configuration:

[mypy]
check_untyped_defs = true
disallow_untyped_defs = true
ignore_missing_imports = true
no_implicit_optional = true
warn_redundant_casts = true
warn_return_any = true
warn_unused_ignores = true

Longer variable names can make your code more readable. For example, I can't tell what q is without reading most of the code, and even then it might be unclear, especially since it's an "intermediate" variable (neither a parameter nor a return value).
You could use a set of constants to define the directions in a more human readable form such as DIRECTION_WEST = (-1, 0).
Inlining such as if foo: continue is generally frowned upon, since it makes it harder to skim the code vertically.
Your docstring could include a definition of the problem, possibly by simply copying from the LeetCode website.
Python has exceptions, and it's recommended to use them rather than special return values to signal a problem.