Reputation: 4043
I've had someone tell me to replace the commonly used constants in the code with variables. For example:
if a > 50:
with something like:
b = 50
if a > b:
considering the fact that the number 50 is commonly used around my code (e.g for comparisons like these). So how is that actually better? Are variables for this purpose more memory-friendly or is it just for the sake of the coding style? I'm using Python 3.4
Upvotes: 1
Views: 605
Reputation: 43495
Since you are using Python 3.4, I would suggest replacing all the magic numbers with IntEnum
.
This will make your code easier to understand and easier to modify if necessary.
And a benefit of using enums is that you cannot accidentally change their values, as you can with variables.
Consider this code;
import os
from hashlib import sha256
def compare(src, dest):
"""
Compare two files.
Arguments
src: Path of the source file.
dest: Path of the destination file.
Returns:
0 if source and destination are different
1 source and destination are identical
2 destination doesn't exist
3 source doesn't exist
"""
xsrc, xdest = os.path.exists(src), os.path.exists(dest)
if not xsrc:
return 3
if not xdest:
return 2
with open(src, 'rb') as s:
csrc = sha256(s.read()).digest()
if xdest:
with open(dest, 'rb') as d:
cdest = sha256(d.read()).digest()
else:
cdest = b''
if csrc == cdest:
return 1
return 2
This code in itself is not unclear. It is documented and all. But once you try and use the return value, you'll constantly have to refer to the docs.
Can you tell why the code below does what it does? Not without referring to the documentation for compare
.
res = compare(file_a, file_b)
if res == 1 or res == 3:
pass
elif res == 0:
# copy file_a to file_b
Now look at this;
import os
from hashlib import sha256
from enum import IntEnum
class Cmp(IntEnum):
differ = 0 # source and destination are different
same = 1 # source and destination are identical
nodest = 2 # destination doesn't exist
nosrc = 3 # source doesn't exist
def compare(src, dest):
"""
Compare two files.
Arguments
src: Path of the source file.
dest: Path of the destination file.
Returns:
Cmp enum
"""
xsrc, xdest = os.path.exists(src), os.path.exists(dest)
if not xsrc:
return Cmp.nosrc
if not xdest:
return Cmp.nodest
with open(src, 'rb') as s:
csrc = sha256(s.read()).digest()
if xdest:
with open(dest, 'rb') as d:
cdest = sha256(d.read()).digest()
else:
cdest = b''
if csrc == cdest:
return Cmp.same
return Cmp.differ
So if you now try using it, it looks like this;
res = compare(file_a, file_b)
if res == Cmp.same or res == Cmp.nosrc:
pass
elif res == Cmp.differ:
# copy file_a to file_b
You can understand this without referring to the docs for compare
. You probably won't even have to check on the specifics of the Cmp
enum.
Upvotes: 0
Reputation: 387557
The idea behind replacing constants with variables that are set up in a central place is less about memory management but more about application management. Constants are supposed to be constant, but very often, changes to the application’s requirements require adjustments in the value of these constants.
So if you have a check a > 50
, maybe at some point in the future it will be required to check a > 60
. In that case, you have to update your code to reflect that new requirement.
The idea with constants is now, that you only have to adjust these numbers in a central place. For example, you could have a constants.py
module that declares all the important constants in a central place, and you only ever perform a check a > constants.SOME_THRESHOLD
. In that case, when the requirements change, you only need to change the constants value in a single place, instead of hunting down all the places where that constant is being used.
This is even more important, if you have different constants that have the same value but mean different things. For example, there are two different constants being used for checks which both start out with a value of 50
. Now for one of these constants, the value should be changed to 60
. With actual values in your code, you now need to decide at every occurrence of that 50
whether it is supposed to be 60
or stay at 50
. But with a centralized constant, you just need to update the constant and everything will work correctly.
Having constant variables also gives you the benefit that these constants get an actual name. A 50
can mean anything; but if there’s a variable name, you can give it a name that describes what it is supposed to mean.
Of course, having such centralized constants doesn’t make sense for everything. You should decide on a case by case basis which constant values would be appropriate to be extracted as a constant variable and which don’t.
As far as memory goes, of course declaring a variable at some point requires that variable to be stored somewhere. But variables are super cheap, so it doesn’t really matter. And the value will most likely be recycled anyway: For small integers, Python even reserves actual constant integer objects as a cache.
Upvotes: 6
Reputation: 632
Generally magic numbers are considered bad style. When I'm reading your code, it might not be clear what "50" is supposed to mean. Minutes? Dollars? That's why you do something like MAX_MINUTES=50. Depending on the code that might be very helpful for someone else to understand what is going on there.
Upvotes: 1