Amir Afianian
Amir Afianian

Reputation: 2795

How to change __init__ param before instantiation

I want to achieve a simple interface as such:

IsAllowed(perms=[IsAdmin(runtime_value) | HasPerm('*')])

Since some variables upon instantiation are not available, I have to postpone the evaluation of perms=[IsAdmin() | HasPerm('*')] to later point in time (when __call__ is called on IsAllowed by the FastAPI webframework I'm using). The closest I have come to achieving that is the following:

IsAllowed(perms = lambda runtime_value: [IsAdmin(runtime_value) | HasPerm('*')])


class IsAllowed(object):
    def __init__(self, perms=None):
        self.perms = perms
    def __call__(self, runtime_value):
        result = self.perms(runtime_value)
        return result

No my question is, is there any way to keep the interface as IsAllowed(perms=[IsAdmin(runtime_value) | HasPerm('*')]) but, somehow inject the lambda into it before instantiation?

I am thinking of maybe metaclasses or leveraging the __init_subclass__ hook? Any ideas?

Upvotes: 3

Views: 59

Answers (1)

eightlay
eightlay

Reputation: 542

If I understood your question correctly, you want something like that:

from typing import Any


class IsAllowed(object):
    def __init__(self, *perms):
        self.perms = perms


def __call__(self, args: list[Any]):
    return all(self.perms[i](args[i]) for i in range(len(args)))

checker = IsAllowed(IsAdmin, HasPerm)
checker(“somevalue”, “*”)

UPDATE:

For the cases mentioned in the comment section:

from typing import Any, Callable


class Operation:
    pass


class Or(Operation):
    def __init__(self, *funcs: Callable[[Any], bool]) -> None:
        self.funcs = funcs
        
    def __call__(self, val: Any) -> bool:
        return any(func(val) for func in self.funcs)
    
    def __or__(self, other: Operation) -> Operation:
        return Or(*self.funcs, other.func)


class Predicate:
    def __init__(self, func: Callable[[Any], bool]) -> None:
        self.func = func
        
    def __or__(self, other) -> Or:
        return Or(self.func, other.func)
    
    
def Is1(x: int) -> bool:
    return x == 1
    
    
def Is2(x: int) -> bool:
    return x == 2
    
    
def Is3(x: int) -> bool:
    return x == 3
    
    
def main() -> None:
    pred1 = Predicate(Is1)
    pred2 = Predicate(Is2) 
    pred3 = Predicate(Is3)
    
    fancy_pred = pred1 | pred2 | pred3
    
    for i in range(5):
        print(fancy_pred(i))


if __name__ == "__main__":
    main()

Upvotes: 1

Related Questions