-
Notifications
You must be signed in to change notification settings - Fork 24
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
f(**dict) does not work #153
Comments
note, /documents/Utilities/pyson/venv/ is just the location of my virtual env to test this.
beartype 0.18.5 |
dispatch happens on positional arguments. |
@nstarman is right! :) @Wouter1 to use dispatch, your arguments must be given as positional arguments. Plum's design of multiple dispatch closely mimics how it works in the Julia programming language. The Julia docs are super good resource. :) |
@nstarman @wesselb I'm looking to make a workaround, maybe you can give me some suggestions? I'm trying to use introspection with get_type_hints but it seems not working properly.
gives but when I use dispatch it doesn't
gives [] instead Am I missing something? |
Hey @Wouter1! Could you give some more context about what you're trying to achieve? One alternative is to splat using only a single from plum import dispatch
class B:
@dispatch
def __init__(self, x: int):
self.b = x
arguments = (1,)
b = B(*arguments) Another alternative is to avoid splatting all-together and just directly write |
Thanks, yes I understood that I need to make a list instead of a dict. What I need to do is construct an clazz instance using the arguments I have in a dict. So I need to call clazz(**dict). Except that it won't work if the clazz has overloaded constructors using plum. So yes I need to convert the dict to a list as that's the only available route with plum. However the problem is that get_type_hints also isn't working when the After a day of searching however I found a maybe-workaround. It appears that inspect.signature works both with normal and with @dispatch-ed methods.
|
If you really need to splat argument from a dictionary, a simpler alternative is to convert the dictionary to positional arguments by using a wrapper method: from plum import dispatch
class B:
def __init__(self, x: int):
self._init(x)
@dispatch
def _init(self, x: int):
self.b = x
b = B(**{"x": 1}) |
@wesselb I'm not sure if I understand. You now have only 1 |
Unfortunately inspect.signature is sometimes returning just a string instead of a real class for the argument types. After a lot more searching it shows that inspect.signature is affected by the use of https://docs.python.org/3/library/inspect.html I can not prevent users of my library from importing that, and they may need it for good reasons. I can not quite comprehend why python makes what looks like a trivial task lead you into a maze of partially-functioning alternatives. Do I fundamentally misunderstand something? How do I get a proper signature of a method/function, even if it is |
@Wouter1 here is an example with two initialisation methods: from plum import dispatch
class B:
def __init__(self, x: int | str):
self._init(x)
@dispatch
def _init(self, x: int):
self.b = x
@dispatch
def _init(self, x: str):
self.b = int(x)
b1 = B(**{"x": 1})
b2 = B(**{"x": "1"}) You can extend this pattern to multiple arguments too. |
@wesselb Thanks for the explanation. But this is not "two initialization methods". It's just one with a catch-all argument. This is not overloading. And it assumes I can rewrite the classes that I need to create from the dict. For instance, what if you have init/1 and init/2 for instance? Like init(int) and init(str,str) ? The next step would be using a general vararg. And then we're exactly where we are now: you can not infer the types anymore and I can't build the list from the dict. |
You can use default arguments: from plum import dispatch
class B:
def __init__(self, x = None, y = None):
self._init(x, y)
@dispatch
def _init(self, x: int, y: None):
self.b = x
@dispatch
def _init(self, x: str, y: str):
self.b = int(x) + int(y)
b1 = B(**{"x": 1})
b2 = B(**{"x": "1", "y": "2"}) I agree that it's not an ideal solution, but dispatch currently requires positional arguments, so you will require a workaround of this sort. I don't think this particular workaround is so bad. |
General variable arguments like |
@wesselb Thanks for the suggestions and thinking along!. |
Could you elaborate on what you mean by not being able to infer the types anymore? The idea of dispatch is that you specify types for every function argument and then choose the method based on the types of the given arguments (in this case, keys in the dictionary). This in particular means that you have to name and specify the types of all keys in the dict.
Technically, you could do something like this: from plum import dispatch
class B:
def __init__(self, x = None, y = None):
print("Old init!")
old_init = B.__init__
def new_init(self, x = None, y = None):
old_init(x, y)
new_init_inner(self, x, y)
@dispatch
def new_init_inner(self: B, x: int, y: None):
self.b = x
@dispatch
def new_init_inner(self: B, x: str, y: str):
self.b = int(x) + int(y)
B.__init__ = new_init Though of course this might not be desirable depending on your use case. |
I think the confusion stems from what you mean by "you" in "you specify types". Let me try to explain in another way. Let's define my software as a method create( List[class names:str], description:dict) -> class_instance What happens is that the software searches the list of classes for one matching the description. Then it takes the constructor arguments from the description and creates an instance of that class I am NOT writing the classes, nor the description. That's done by the users of my library. My code needs to search the actual classes, check their constructors, and match them with the data in the description. I would like to support |
Hmm, one possible solution would to not pass the description as a dictionary but as plain arguments, and pass these to the class: from plum import dispatch
def instantiate(cls, *args, **kw_args):
return cls(*args, **kw_args)
class MyClass:
@dispatch
def __init__(self, x: int):
self.x = x
a = instantiate(MyClass, 1) Would something like this be acceptable? |
@wesselb You may consider adding this to the doc in the keyword arguments section as this is a fairly reasonable workaround (but not as good as it could be). |
@Moosems Thanks! Good suggestion. I've added this to the docs. |
How do I call an overloaded constructor if I have a dict with the call args ?
If the constructor is not overloaded like this it works fine using **
but when the constructor is overloaded this doesn't. The error message does not make any sense either.
Traceback (most recent call last):
File "/documents/Utilities/pyson/venv/lib/python3.8/site-packages/plum/function.py", line 421, in _resolve_method_with_cache
return self._cache[types]
KeyError: (<class 'main.B'>,)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/documents/Utilities/pyson/venv/lib/python3.8/site-packages/plum/function.py", line 341, in resolve_method
signature = self._resolver.resolve(target)
File "/documents/Utilities/pyson/venv/lib/python3.8/site-packages/plum/resolver.py", line 168, in resolve
raise NotFoundLookupError(f"
{target}
could not be resolved.")plum.resolver.NotFoundLookupError:
(<__main__.B object at 0x7faebc3e7820>,)
could not be resolved.During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "", line 1, in
File "/documents/Utilities/pyson/venv/lib/python3.8/site-packages/plum/function.py", line 489, in call
return self._f(self._instance, *args, **kw_args)
File "/documents/Utilities/pyson/venv/lib/python3.8/site-packages/plum/function.py", line 398, in call
method, return_type = self._resolve_method_with_cache(args=args)
File "/documents/Utilities/pyson/venv/lib/python3.8/site-packages/plum/function.py", line 427, in _resolve_method_with_cache
method, return_type = self.resolve_method(args)
File "/documents/Utilities/pyson/venv/lib/python3.8/site-packages/plum/function.py", line 350, in resolve_method
method, return_type = self._handle_not_found_lookup_error(e)
File "/documents/Utilities/pyson/venv/lib/python3.8/site-packages/plum/function.py", line 394, in _handle_not_found_lookup_error
raise ex
plum.resolver.NotFoundLookupError: For function
__init__
of__main__.B
,(<__main__.B object at 0x7faebc3e7820>,)
could not be resolved.The text was updated successfully, but these errors were encountered: