-
Notifications
You must be signed in to change notification settings - Fork 2
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
Simplify creation of immutable collections #12
Comments
I am in favor of the general idea, but with different terminology. I am uneasy about using Possible alternate names for the
@berquist , any thoughts on this? |
Regarding the problem with changing the behavior of Another idea: We can also do |
@ConstantineLignos : As long as we deprecate-and-reintroduce, I am ok with I'd prefer to avoid I wonder if this is the sort of thing where we should come up with some options and then poll all the Python programmers in the office. |
You could refuse to take a plain
+1, this is the above.
"Explicit is better than implicit." I think
This is the most Pythonic approach mentioned, rather than static methods. For others with a non-Java background, it's what we expect from a more minimal Python API (rather than something like Django or SQLAlchemy). Then you run into the danger of mixing API styles, and consistency is more important. |
Following on the more pythonic route for a moment, it would make sense to have If we were to stop there, we'd be no worse than any other standard Python datatype. So I do think the So let me stop there: @berquist and @rgabbard, would you both support |
One last dump of ideas about the name of the *args/**kwargs version: |
Would it be better to have Hmm - another question about
Would this prevent us from simply returning the input unchanged when it is already an immutable set? ImmutableDict initializationNote that here the |
Ah, ok, it looks like dict ordering becomes part of the standard in 3.7, so it's sort of a question of what versions we want to support. |
Oh, nevermind, type annotations. :-) |
Not sure if this is useful, but I am trying to actually use the library for the first time, and wanted to share my thought process after reading the tests and then skimming the code. rows = [('a', 'b'), ('a', 'c'), ('c', 'd')]
print(ImmutableListMultiDict.of({row[0]: row[1] for row in rows})) This, of course, doesn't work, because the intermediate dictionary loses all your information. Then how is an instance constructed? I saw something about a builder, but that probably means it can't be constructed with a one-liner, I only saw b = ImmutableListMultiDict.builder()
b.put_all_items(rows)
x = b.build() but this reads like Java, not Python. Everything could be chained together (like on https://github.com/isi-vista/immutablecollections/blob/master/immutablecollections/immutablemultidict.py#L164). Wait, this works, if only I had read the docstring the first time: y = ImmutableListMultiDict.of(rows) and this of course doesn't work:
Maybe this is just a case of not reading the documentation well enough, but I rarely think about static methods when writing Python; for better or worse, constructing the object directly via |
@ConstantineLignos : We could consider raising an exception if someone passes a @berquist : Thanks for the comments! I know "create-by-calling-constructor" is common in the Python world, but I (personally) think it is a terrible habit. The modern Java habit of keeping constructors private and using static factory methods almost always leads to clearer, more flexible, and more testable code. (I just this morning ran into a case where I'm going to need to rewrite code in a Python library because it needlessly does initialization from a file passed to the constructor when I instead need to initialize it directly with information from another source. Argh.). That said, module-level factory functions are a decent, more Pythonic alternative to static factory methods (why they are considered superior in Python-land, with their decreased discoverability compared to static factory methods, is beyond me, however. I guess the less something looks like Java, the more Pythonic it is?
be more Pythonic-ly intuitive? |
So after another slack chat with @rgabbard I'm leaning toward a design with the goal of matching the built-in collection types as much as possible. Thus the default way to build things is using a lowercase function name (replacing Here's a sketch:
(The inheritance on each class reflects the current implementation, so let's not dig into it right now.) A few things I'd like to discuss:
|
re: 4 - note that the built-in frozen set doesn't support
|
Overall, I like this. A few comments:
Another (orthogonal) option is to put |
Summary of decisions from Slack conversation with @ConstantineLignos :
Note in particular that things like For |
Here's the updated code to match what @rgabbard and I worked out:
#18 covers sorting out the implementation of the order check for |
We currently have
ImmutableSet.of(iterable, ...)
, so when creating an ImmutableSet you have to write things likeImmutableSet.of([1, 2, 3])
where you create the iterable (which must not be a set if you want the ordering to be reliable).I originally created the above API to move away from the Guava approach which I found confusing. In Guava-land, you effectively have a
*args
version withImmutableSet.of(<varags>)
andImmutableSet.copyOf(<iterable>)
. The latter can be confusing to the user because it is not necessarily the case that a copy is made, and they might (as I did at first) shy away fromImmutableSet.copyOf(X)
whereX
may already be anImmutableSet
.I do find that in terms of usability, having to write the extra
[]
,()
, or{}
when you are creating constants is a bit of a blemish on the API design and can also cause some readability issues with parens when you haveImmutableList.of((1, 2, 3))
due to double parens. (In ELICIT, in fact most all of our usage of the immutable collections is as constants, so we are always mildly annoyed by these extra characters.) So I'd like to revisit the decision I made early on with the following proposal designed forImmutableSet
andImmutableList
(possibly other collections if it makes sense, I'll just writeSet
everywhere below):ImmutableSet.of(*args)
: create anImmutableSet
fromargs
. Note that since the output is always ordered, you are guaranteed to not get in trouble with the intuitive but bad ideaImmutableSet.of({1, 2, 3})
which will not preserve order.ImmutableSet.from(iterable)
returns anImmutableSet
made from the iterable. It's also a good time to bite the bullet and makerequire_ordered_input
default toTrue
for this method. I think there's no better time to address that than when making another breaking API change.I'm very open to changes to the of/from terminology, the point is really just the two ways of creation and that neither has "copy" in the name.
Some benefits to note:
ImmutableDict.of(**kwargs)
which allows you to do things likeImmutableDict.of(a=1, b=2)
.@rgabbard What do you think?
The text was updated successfully, but these errors were encountered: