New Caching decorators not working with Classes with __init__

I have a page using a class with __init__ method, using self for all methods and attributes. And I tried for the first time using @st.cache_data on my def pipeline(self, path) function. It is working with st.cache (just gives a warning about deprecated method and etc). But when I use the new one, it gives this error:
TypeError: SellingPrices.pipeline() missing 1 required positional argument: 'self'

It treats self like a parameter that should be passed on the function calling, so I tried excluding this parameter from the cache by setting an underscore before the self variable as the docs explained but the same error message continues.

I really like making my streamlit app using classes with __init__ methods, and I’d like to use these cache methods as well, so could anyone please help me with that?

The caching function stores an output associated to a specific input, so it doesn’t strike me as something I’d want to put on a method. Can you create a builder function and cache that?

@st.cache_data
def build (path):
    return SellingPrice.pipeline(path)

Down below is the skeleton of my page using this class, and I’d like to use the caching decorators for most of these methods. I like doing like this as this keeps the code more organized, readable and better for maintenance. Especially because I can call the methods for some attributes “before” instantiating them, by using self.method in the __init__ constructor method.

class SellingPrices:
  def __init__(self):
    # quite a lot of variables using the methods bellow

  def pipeline(self, path):
    #...
    return df.reset_index(drop=True)

  def group_df(self, ...):
    #...
    return getattr().reset_index()

  def filter_df(self, ...):
    #...
    return df[(df[feature] >= start_date) & (df[feature] <= end_date)]

  def reset_date(self, *args):
    st.session_state['start_date'] = args[0]
    st.session_state['end_date'] = args[1]

  def style_df(self, df):
    return df.style.format(...)

  def get_stats(self, ...):
    #...
    return st.metric())

  def get_graph(self, ...):
    #...
    return st.plotly_chart(fig, use_container_width=True, theme='streamlit')

  def view(self):
    # streamlit code for front-end stuffs

SellingPrices().view()

Btw, I’m not a Python or Streamlit expert. Just trying to make good code here :slight_smile:

I’m just not sure it makes any sense to put a caching decorator directly on a method. The caching function has to index the output it is caching according to the input and I wouldn’t assume a priori that it’s made to be compatible with self as a parameter. (Indeed, your test appears to confirm it is not compatible with self as a parameter.)

If you want to adapt a decorator to accept a method, you can do that
 python - Using the same decorator (with arguments) with functions and methods - Stack Overflow However, I think getting the desired results would be quite tricky for a caching decorator, specifically. The caching function does not save side effects; it only saves the final output. So caching any function or method that requires a change not reflected in the actual, returned value is a no-go. (Your reset_date method is just not something you’d cache as its purpose is the change it makes to session state, not the object it is returning.)

I played around with the idea a little, but it is very convoluted to make it do want you’d likely want even in the subset of methods where caching might make some sense.

I guess I’ll have to choose between coding with classes, in which I prefer because of better code’s organization, readability and reliability. Or coding in a procedural way so I can use the streamlit caching decorators on my page.

Hi all :wave:

This is a known issue that was fixed just last week. Class member functions will be properly cached in the upcoming Streamlit v1.19.0 this week:

5 Likes

Just upgraded to 1.19 and it still does not work:

UnhashableParamError: Cannot hash argument ‘self’ (of type gooddata.execute.GoodDataExecutor) in ‘execute_stored_insight’.

To address this, you can tell Streamlit not to hash this argument by adding a leading underscore to the argument’s name in the function signature:

@st.cache_data
def execute_stored_insight(_self, ...):
    ...
1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.