I'll go out on a limb and say: No, this is a terrible idea.
It's just a special case of reusing a variable, which is a bad idea - mainly because it makes it hard to understand what a variable contains at any given point in the program flow. See e.g. Should I reuse variables?
About your points: The points you raise are valid, it's just that reusing the variable is not a good solution :-).
a) it conveys that the response variable contains basically the same
information, just 'transformed' into a different type
Providing this information is a good idea. However, don't do this by using the same variable, because then you obscure the fact that it the information was transformed. Rather, use names with a common pre-/postfix. In your example:
rawResponse = urlopen(some_url)
[...]
jsonResponse = response.read()
[...]
responseData = json.loads(response)
[...]
This makes it clear that the variables are closely related, but also that they do not contain the same data.
b) it conveys that the earlier objects aren't going to be needed any
further down the function, since by reassigning over their variable
I've made them unavailable to later code.
Again, communicating this "no longer needed" is good, but don't do it by reusing the variable: The reuse assignement will usually be hard to see, so you only confuse the reader.
Rather, if a variable lives long after its last use, that is an indication the method/function is too long. Split the part with the short-lived variables into a sub-function; that makes the code easier to read, and limits the variable lifetime.
Note: I usually even go one step further than not reusing variables, and try to even only assign a value once (i.e. never change the value, make it immutable). This is an idea mainly from functional languages, but I found it can make code much clearer. Of course, in non-functional languages, you sometimes need to change a variable (obvious example being a loop variable), but once you start looking, you'll see that in most cases a "fresh" variable makes for more readable and less bug-prone code.
The Boost libraries for C++ include an article on dimensional analysis that presents a sample implementation of handling units of measure.
To summarize: Units of measurement are represented as vectors, with each element of the vector representing a fundamental dimension:
typedef int dimension[7]; // m l t ...
dimension const mass = {1, 0, 0, 0, 0, 0, 0};
dimension const length = {0, 1, 0, 0, 0, 0, 0};
dimension const time = {0, 0, 1, 0, 0, 0, 0};
Derived units are combinations of these. For example, force (mass * distance / time^2) would be represented as
dimension const force = {1, 1, -2, 0, 0, 0, 0};
Imperial versus SI units could be handled by adding a conversion factor.
This implementation relies on C++-specific techniques (using template metaprogramming to easily turn different units of measurement into different compile-time types), but the concepts should transfer to other programming languages.
Best Answer
I'd generally use an object rather than a KeyValuePair or Tuple in most cases. First, when you come in 6 months later to make changes, it is alot easier to figure out what your intent was earlier rather than wondering what
Tuple t
is and why it has those funny values. Second, as things grow and change you can easily give your simple data transfer objects behavior as required. Need to have two name formats? Easy, just add appropriate ToString() overloads. Need it to implement some interface? No problem. Finally, there really is nearly zero overhead to creating a simple object, especially with automatic properties and code completion.Bonus Protip: if you want to keep these objects from polluting your namespace, making private classes inside of classes is a great way to keep things under wraps and prevent strange dependencies.