It depends on how many times you call the outer function, and on whether it returns the nested function or not.
If you have a nested function, the statement def b(...) not only assigns a new function object to the name b, but also creates a closure that encapsulates the namespace of a for b to access. This is temporary for the duration of the execution of a if a does not return b, i.e., make it non-local.
If b is a separate function object, it gets assigned to a function object just once, when the module code is run on import. It has no non-local closure namespace.
In both cases, the code of b is compiled into a code object exactly once, on module import. The function objects that are created are relatively cheap by comparison. The function objects reference the code singleton. The function objects will also always have a reference to the global namespace in both cases.
So if b is nested in a, the interpreter will create a new function object around the code of b every time a is called. It will also create a closure for the namespace of a.
If you intend to call a at least once, the non-nested version is less memory intensive since does not create closures or create multiple function objects.
Sometimes, however, design decisions are made to sacrifice some speed and memory usage for legibility, maintainability, or other considerations. You are using python after all. If, for example, b ought to stay hidden from the rest of the code, I would likely posit that the nested solution is better despite being generally more memory and processor intensive. An example is where b is an implementation detail of a, like the recursive portion of a function.