I start my question by describing the use case:
A context-menu should be populated with actions. Depending on the item for which the menu is requested, some actions should be hidden because they are not allowed for that specific item.
So my idea is to create actions like this:
edit_action = Action("Edit Item")
edit_action.set_condition(lambda item: item.editable)
Then, when the context-menu is about to be opened, I evaluate every possible action whether it is allowed for the specific item or not:
allowed_actions = list(filter(
lambda action: action.allowed_for_item(item),
list_of_all_actions
))
For that plan to work, I have to store the condition in the specific Action instance so that it can be evaluated later. Obviously, the condition will be different for every instance.
The basic idea is that the one who defines the actions also defines the conditions under which they are allowed or not. I want to use the same way to enable/disable toolbar buttons depending on the item selected.
So that is how I tried to implement Action (leaving out unrelated parts):
class Action:
_condition = lambda i: True
def set_condition(self, cond):
self._condition = cond
def allowed_for_item(self, item):
return self._condition(item)
My problem is now:
TypeError('<lambda>() takes 1 positional argument but 2 were given')
Python treats self._condition(item) as call of an instance method and passes self as the first argument.
Any ideas how I can make that call work? Or is the whole construct too complicated and there is a simpler way that I just don't see? Thanks in advance!
Update: I included the initializer for _condition, which I found (thanks @slothrop) to be the problem. This was meant as default value, so allowed_for_item() also works when set_condition() has not been called before.
Setting the class attribute
_conditionto a function (whether through a lambda or adef) makes that function into a method: i.e. when accessed as an instance attribute, the instance is inserted as the first argument to the function call.So this:
does the same as this:
While these two are equivalent, the
defversion is more familiar, so the problem with it (lack ofselfin the signature) is more obvious.The underlying mechanism for this is summarised on the Python wiki:
Possible solutions are:
Set the default as an instance attribute (the solution you arrived at)
Add the extra parameter to the lambda, so
_condition = lambda _self, _i: TrueMake the method static:
_condition = staticmethod(lambda _i: True)