illright
illright

Reputation: 4043

Do constants take up more memory than variables in Python?

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

Answers (3)

Roland Smith
Roland Smith

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

poke
poke

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

Robin Davis
Robin Davis

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

Related Questions