"import mod" defines mod, which didn't exist in your module before.
"from mod import val" defines val, which didn't exist in your module before.
"import mod" defines mod, which didn't exist in your module before.
"from mod import val" defines val, which didn't exist in your module before.
Most languages have scopes. Things may exist in your scope, so you can access them. Or they may exist but in some other scope not visible to you where you currently are. Symbol tables track what is in a scope.
A variable is a name associated with a storage location. The location could be a stack, heap, register, or something way more abstract as long as it behaves as a "place" you can store a value into and read from.
When a variable is created, a name is defined and some storage is arranged for. But that name is also added to a scope. (If it weren't, it would have a name, but nobody could see it.)
There's no reason, once the variable has been added to one scope, that it can't be added to another scope. That is what "import" statements do in a lot of languages.
Python has apparently chosen a very different approach for imports, which is to create a new variable. This isn't necessarily wrong or right, it's just not something I had ever seen a language do before.