-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
acccessor extending approach limits functional programming approach, make direct monkey-patching also possible #1080
Comments
I don't see a conflict between encouraging accessors and making duplicate methods/functions. If you want duplicate method/functions, you can totally do that on an accessor class: import functools
import xarray
@xarray.register_dataarray_accessor('custom')
class CustomAccessor(object):
def __init__(self, data_array):
self._data_array = data_array
def patch_custom(func):
@functools.wraps(func)
def method(accessor, *args, **kwargs):
return func(accessor._data_array, *args, **kwargs)
setattr(CustomAccessor, func.__name__, method)
return func
@patch_custom
def square(data_array):
return data_array ** 2
xarray.DataArray([1, 2, 3], dims='x').custom.square()
# <xarray.DataArray (x: 3)>
# array([1, 4, 9])
# Coordinates:
# * x (x) int64 0 1 2 If you really desire, you can even make method-like accessors by adding a
or even simpler
I would definitely discourage writing too many of such methods, though. Finally, it seems like the simplest solution to your concern about needing methods for |
Thank you for your response. I still don't understand why you are pushing accessors in place of methods to such an extent. Is it because of namespace growth/conflicts? There are already many methods like While the solutions you presented are usable, they seem like workarounds and somewhat redundant or add extra like overhead (in terms of writing code). Registering extra dataset accessors where DataArray method application would do seems again redundant.
Could you please give some clear arguments why you discourage the use of normal methods? The two arguments listed in the docs don't really make a compelling case against method monkey-patching, because
I'm not trying to say that the accessor approach is wrong, I'm sure it makes sense for certain plugins. I'm just trying to share my experience with a very similar case where the simpler method approach turned out to be satisfactory and I think enabling it would increase the chances of more xarray plugins (which may not need accessor logic) coming to life. Btw, perhaps it might be better to (perhaps optionally) issue a warning when overriding an existing class attribute during registering instead of completely refusing to do so. |
Indeed. My thinking was the A stricter approach would have been to put everything under an attribute just for extensions, e.g.,
I'll add a note about the value of namespaces to the doc.
We could certainly turn this off (optionally) if there are cases where it does the wrong thing. Could you go into this in a little more detail, perhaps with a concrete example? My expectation was that this should have minimal design or performance downsides. |
The namespace argument doesn't seem very convincing since you already implement many methods which may shadow variables (mean, diff). By limiting control of the namespace you make some uses somewhat inconvenient. If you want users to use DataArray as a general and universal and also extensible container, limiting its namespace goes against that. If they shadow vars by their methods, that's their decision to make. While it may seem cleaner to have a stricter API, in real use cases users care more about convenient code access than where it came from. And when they look at the method object it will clearly tell them where it was defined. Python's introspection capabilities are powerful enough that users can find out such information. What I meant by the 2. point was that in many cases one just needs a simple method and with the accessor approach one has to write extra lines of code like the ones you suggested earlier that may later seem cryptic. Caching of the accessor can be indeed useful, just not always. If you want people to develop plugins, make it as simple as possible and yet also advanced for those who require it. And then there"s also the problem of accessors not being usable in functional programming paradigms. Tl;dr: accessors have benefits (namespace containment, caching) but also limitations (not functional paradigm, overkill sometimes). Give users more control over methods and you'll get more plugins. On November 6, 2016 2:22:44 PM GMT+01:00, Stephan Hoyer notifications@github.com wrote:
Sent from my Android device with K-9 Mail. Please excuse my brevity. |
I don't really agree here. Code is read in text form more often than it is interactively explored. At Google, our Python style guide actually prohibits writing import like
xarray objects are already non-threadsafe, in the same way that the built-in Finally, I'll note that we also have the You are certainly welcome to monkey patch -- that's the Python philosophy, after all -- but I'm not going to recommend it or make it easy. But I would even subclassing before considering monkey patching -- at least then your methods are contained to your own code, instead of contaminating a global namespace. |
Good point, in that case explicit namespacing indeed helps.
A module-level namespace has nothing to do with the class namespace, but I see you try to tie them, which makes sense in relationship with the argument about reading code in text form. However, that may not be clear for Python programmers as those namespaces are not tied in reality, better mention it in the docs. BTW, if you are enforcing some specific style guide, please note that in the docs. And I hope you strike the right balance between style complacency and universality.
My problem with non-functional paradigms lies more in the
That is indeed a good alternative, just not sure my colleagues will like the transition from |
I think we probably need to agree to disagree here. I will update the docs in response to feedback (which is greatly appreciated!) and when I do so I will close out this issue.
This seems too magical to me, but you are welcome to make another issue to see what others think. |
Sorry for chiming in here. Using the example above, if we have the choice between:
Of course, (1) is attractive because straight forward (as the dev of a small xarray accessor, I also forget very often that I have to add an attribute between the dataset and my function call). But (2) has the huge advantage that it clearly says where the code of the function is found, and where to ask questions when things do not work as expected. (3) is OK for me since it is very explicit, but I find that (4) is quite ugly. |
Thank you for continuing this discussion even though you didn't agree with the initial proposal. |
Hi, thatnks for creating and continuing development of xarray. I'm in the process of converting my own functions and classes to it which did something very similar (label indexing, plotting, etc.) but was inferior in many ways.
Right now I'm designing a set of functions for digital signal processing (I need them the most, though inteprolation is also important), mostly lowpass/highpass filters and spectrograms based on
scipy.signal
. Initially I started writing adsp
accessor with such methods, but later I realized, that this accessor approach makes it quite hard to do something likedataset.apply(lowpass, 5)
. Instead, one has to do something likedataset.apply(lambda d: d.dsp.lowpass(0.5))
which is less convenient than the clear functional programmingapply
approach.I agree that making sure that adding a method to the class does not overwrite something else is a good idea, but that can be done for single methods as well. It would be even possible to save replaced method somewhere and replace them later if requested. The great advantage is that the added methods can still be first-class functions as well.
Such methods cannot save state as easily as accessor methods, but in many cases that is not necessary.
I actually implemented something similar for my DataArray-like class (before xarray existed, now I'm trying to convert to
xarray
) with such plugin handling (below with slight modifications forDataArray
). Let me know what you think.The text was updated successfully, but these errors were encountered: