In your C example the comnpiler knows a, b and c are integers because they are declared as int, and they will always (as far as the compiler is concerned) be of int type and hold an int value, so at compile time the generated code for an expression like a+b will simply call the addition operator for int safe in the knowledge that the variables have an int value.
Python doesn't let you declare the type of a variable so under the hood what python does is keeps the type and the value together - they are indivisible - so the type of the value held in a variable is always known. You can inspect this with type() and isinstance(). Yes, the type is initially deduced from literals e.g. 1 is an integer, 1.0 is a float, 'asd' is a string. Then once a value is in a variable, the type of the variable is used in expressions to allow calling the correct operator function for e.g. + where adding ints is different from adding (concatenating) strings - and the type of intermediate results is retained right through the calculation of the value and type of the expression as a whole. Python also knows which types can be coerced into related types, e.g. Integer to float, and which can't. In Python, when you write an expression like a+b, at runtime the interpreter code looks at the types of the values in a and b, and tries to find a matching operator to call, if it can't find one an exception is raised.
Hope that makes sense. There are probably better explanations out there if you search.