Scipio
Scipio

Reputation: 313

Long list of parameters of class method in Python: local, argument or class atribute?

I have a Python class with a number of functions. Evaluating each function requires a long list of constants (a few dozen); the constants are different for each function. Each function will be called many times (millions), so performance is a major concern. What is the proper way to handle this situation, both in terms of readability and in terms of speed? (I'm relatively new to python)

I could make the constants class attributes, but this seems to clash with the safety provided by scopes (each constant is used only locally in a single function). Also, it would be convenient to be able to use the same variable names in different functions. Besides, I find that all the self.s make the code unreadable, especially when trying to keep line length under 79.

I now have the constants defined locally at the start of each function. This is reasonably clear, but I'm not sure if it's optimal in terms of performance? Is new memory allocated and freed for every function call? Especially since you apparently can't declare proper constants in Python.

Maybe it's better to put the parameters for each function in a separate class and pass an object of this class as an argument? This would maintain the proper scopes while ensuring each constant is defined only once?

Upvotes: 0

Views: 216

Answers (1)

Scipio
Scipio

Reputation: 313

I ran some speed tests using the logistic map with single parameter r as an example. Defining r locally at the start of the function gives the best performance, while setting r either as a class attribute or passing it to the function as an argument (individually or as attribute of an instance of a parameter class) raises computation time by roughly 10%. So I suppose the interpreter recognizes the fact that a locally defined r is a constant when the object is created. (Note that I test with only a single parameter, so the difference may be larger in more complicated models.)

In case anyone is curious, here is the code of the test:

import matplotlib.pyplot as plt
import timeit

# Define class
class Mod:

    def __init__(self):
        self.reset()
        # self.r = 3.9 # -> uncomment when testing class attribute
        return

    def reset(self):
        self.x = 0.5

    def step(self): # -> add r when testing argument

        # ARGUMENT
        # self.x = r*self.x*(1-self.x) # +10% computation time

        # CLASS ATTRIBUTE
        # self.x = self.r*self.x*(1-self.x)  # +10% computation time

        # LOCAL
        r = 3.9
        self.x = r*self.x*(1-self.x)

# Length of test
trials = int(1e5)
T = int(1e3)

# r = 3.9 # -> uncomment when testing class attribute

# start timer
t0 = timeit.default_timer()

for _ in range(trials):
    x = []
    m.reset()
    for _ in range(T):
        m.step()  # -> add r when testing argument
        x.append(m.x) # do something with data to avoid interpreter tricks

# print computation time
print(timeit.default_timer() - t0)

plt.plot(x) 
plt.show()

Upvotes: 1

Related Questions