-
Notifications
You must be signed in to change notification settings - Fork 74
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
Support for bound methods at runtime #5
base: main
Are you sure you want to change the base?
Conversation
We'll run into issues both with method/function naming clashes, but also between methods in different classes with the same name. For example I suspect that The two mechanisms to specify the class I see are the following:
Question: Is there some better heuristic that we can use? Is there something about the context of a method other than that |
So in Python 3 the def f(): pass
f.__name__ # 'f'
f.__qualname__ #'f'
class Test:
def f(self): pass
test = Test()
test.f.__name__ # 'f'
test.f.__qualname__ #'Test.f' So a quick fix would be to use the qualname as the first dispatch key, then the arguments, instead of dispatching on the (normal) name and arguments. Effectively, But that's only in Python 3, unfortunately. |
That's really cool. I also tried the following experiment to further convince myself that it worked. In [1]: def print_qualname(f):
...: print(f.__qualname__)
...: return f
...:
In [2]: class Test:
...: @print_qualname
...: def f(self):
...: pass
...:
Test.f I do think that it's important for |
Does |
Good question. It looks like The currentframe bit still bothers me a little -- feels too magicy -- but maybe it is necessary in Python 2, which I agree needs to be supported. |
In general I strongly agree with you about disliking magic. I sort of expect this project to be magical though, so my standards are low. From what I can see This might be a possible sensible approach for your current problem. As long as we always (both for functions and methods) inject the To say this another way I think that if we always use the I'm not certain about the best course of action here. I think some serious thinking may be required. |
Ok, after much thought I don't think it's possible to avoid using Users can still supply custom namespaces, but within a custom namespace class methods and functions will collide if they have the same name (I added a test for this). |
So the problem with this is that different modules can't "democratically" create a function. This democratic definition is for me one of the common motivators of multiple dispatch. What do I mean by this? ExampleLets consider a doubly dispatched variant of # point.py
@dispatch(float, float)
def str(x, y):
return "(%f, %f)" % (x, y)
# peopleNames.py
@dispatch(str, str)
def str(first, last)
return first + " " + last We would like for the same str function to work on both type signatures, even though they are defined in different modules (or even different projects). To accomplish this then one of the projects must create the custom namespace and the other projects must import the namespace from that module. This requires coordination and the choice of a "master" project. This coordination might be difficult. Is this important? I don't know. I actually think that the global namespace is a useful default. It is in line with solutions in C, C++, and Julia. BTW, I apologize for coming back each time with a problem that the current solution can't solve. No solution exists that solves all of these problems. Mostly I'm working out for myself what I think of the various approaches. |
No need to apologize, it's helpful to go back and forth! Your reasoning about the global namespace makes perfect sense, I think I missed the forest for the trees on that one while trying to force a solution. So I think what we've done here is taken a grand tour of all the options and are ending up back at your original solution (which is a good thing!). I guess we must either rely on heuristics (like the current |
Previously, detecting methods depended on looking for a
self
argument which isn't robust, since users could easily call the first argument something other thanself
.But fundamentally, the issue is this: at compile time, there is no difference between a method named
f
and an equivalent function namedf
except that one has more arguments than the other (namely, one usually calledself
). Decoration (and consequently, dispatching) takes place at compile time. Binding the function (and more importantly, making it identifiable as a bound method) takes place at run time, after the dispatching has finished. So how can the dispatcher know if the function is bound and needs to be passedself
?One way around this is to use the
__get__()
descriptor method, which is only called ifDispatcher
is being accessed as a attribute of a class. We make the assumption that if__get__
is called, the user is trying to call a bound method. This PR watches for that situation and adds an instance attribute to the dispatcher. Then when the dispatcher's__call__
method runs, if it finds the instance attribute, it includes it in the function call (taking the place of 'self') and then resets it so it won't add it every time.This still has drawbacks... since functions are dispatched by name, methods can be overwritten by similarly-named functions that are defined later in the program, which causes confusion because Dispatcher will add
self
and then call the most recent function (that isn't a method). Appreciate any thoughts on how to handle this... For now, users should be aware that methods and functions with the same name are all dispatched together. Perhaps that's desirable?