Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot change instance attributes with a Callable without passing in parameterized class #900

Open
DmitriyLeybel opened this issue Jan 10, 2024 · 3 comments

Comments

@DmitriyLeybel
Copy link

Problem:
Param callables do not work as expected given the purpose of the parametrized class.
Per the documentation, the purpose of the callables is partly to modify the attributes of the class.

Invocation parameters are a loose group of types that either contain an executable (callable) object, are invoked to execute some other code, or are set to change the value of some other parameter(s) or attribute(s).

Instead of having to pass self to the instance like so

class ExpEnv(param.Parameterized):
  url_input = param.String('test')
  url_list = param.List([])
  url_submit = param.Action(lambda self: self.url_list.append(self.url_input))

ee = ExpEnv()
ee.url_submit(ee)
ee.url_list

output: ['test']

it would be preferable to simply call the test_class.fn() where it implicitly passes self within the parameter creation like so:

ee.url_submit()

This makes more sense, given that the intent of the callable is to modify the attributes of the instance.

Additional context

Discourse discussion: https://discourse.holoviz.org/t/how-does-param-action-callable-handle-passing-self-to-function-unexpected-behavior-in-conjunction-with-panel/6665

@jbednar jbednar changed the title Cannot change class attributes with a Callable without passing in parametrized class, creating a poor interface and confusion. Cannot change instance attributes with a Callable without passing in parameterized class Jan 10, 2024
@jbednar
Copy link
Member

jbednar commented Jan 10, 2024

Thanks for the good question! I've updated the issue title to indicate that it's not class attributes that are being updated, it's instance attributes, even though they are declared at the class level in the source code. (I.e. the action here updatesurl_list for the instance ee, not the class ExpEnv).

In any case, I agree that typing ee.url_submit() would be vastly more intuitive. How could we achieve that? ee.url_submit returns the actual function provided, which indeed does require the ee argument when called. Is the proposal to return a closure (a partial where self has already been supplied) that we create to wrap around the provided function, instead? I.e., effectively returning lambda: (lambda self: self.url_list.append(self.url_input)(ee) or functools.partial(lambda self: self.url_list.append(self.url_input), self=ee) rather than lambda self: self.url_list.append(self.url_input)? I'm not aware of any other case where we do that sort of doctoring to the actual value of the parameter when we look it up. Maybe it would be safe? Not sure.

@hoxbro
Copy link
Member

hoxbro commented Jan 11, 2024

An alternative could be to set it inside __init__, where self is available.

import param

class ExpEnv(param.Parameterized):
    url_input = param.String("test")
    url_list = param.List([])
    url_submit = param.Action()
    
    def __init__(self, **params):
        super().__init__(**params)
        self.url_submit = lambda *a: self.url_list.append(self.url_input) or self.param.trigger('url_list')


ee = ExpEnv()
ee.url_submit()

I have added *a to make it work with pn.panel(ee) and the or self.param.trigger('url_list') to signal that the url_list has been updated.

@maximlt
Copy link
Member

maximlt commented Jan 11, 2024

self.url_submit = lambda *a: self.url_list.append(self.url_input) or self.param.trigger('url_list')

That would make some code reviewers very unhappy 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants