On Apr 20, 2020, at 13:42, Christopher Barker <pythonchb@gmail.com> wrote:

On Mon, Apr 20, 2020 at 12:17 PM Andrew Barnert <abarnert@yahoo.com> wrote:

A lot of JSON is designed to be consumed by JavaScript, where there is no real line (there is no dict type; objects have both dict-style and dot-style access). So in practice, a lot of JSON maps well to data, a lot maps well to objects, and some is mushily designed and doesn’t quite fit either way, because in JS they all look the same anyway.

Well, sure. Though JSON itself is declarative data.

Sure, it’s a declarative format, it’s just that often it’s intended to be understood as representing an object graph.

In Python, you need to decide how you want to work with it, either as an object with attributes or a dict. But if you are getting it from JSON, it's a dict to begin with. So you can keep it as a dict, or populate an object with it. B ut populating that object can be automated:

an_instance = MyObject(**the_dict_from_JSON)

But unless your object graph is flat, this doesn’t work. A MyObject doesn’t just have strings and numbers, it also has a list of MySubObjects; if you just ** the JSON dict, you get subobjs=[{… d1 … }, { … d2 … }], when what you actually wanted was subobjs=[MySubObject(**d) for d in …].

It’s not like it’s _hard_ to write code to serialize and deserialize object graphs as JSON (although it’s hard enough that people keep proposing a __json__ method to go one way and then realizing they don’t have a proposal to go the other way…), but it’s not as trivial as just ** the dict into keywords.

> But, maybe even more importantly: even if you _do_ decide it makes more sense to stick to data for this API, you have the parallel `{'country': country, 'year': year}` issue, which is just as repetitive and verbose.

only if you have those as local variables -- why are they ?

Apologies for my overly-fragmentary toy example.

Let’s say you have a function that makes an API request to some video site to get the local-language names of all movies of the user-chosen genre in the current year.

If you’ve built an object model, it’ll look something like this:

    query = api.Query(genre=genre, year=datetime.date.today().year)
    response = api.query_movies(query)
    result = [movie.name[language] for movie in response.movies]

If you’re treating the JSON as data instead, it’ll look something like this:

    query = {'query': {'genre': genre, 'year': datetime.date.today().year}}
    response = requests.post(api.query_movies_url, json=query).json
    result = [movie['name'][language] for movie in response.movies]

Either way, the problem is in that first line, and it’s the same problem. (And the existence of ** unpacking and the dict() constructor from keywords means that solving either one very likely solves the other nearly for free.)

Here I’ve got one local, `genre`. (I also included one global representing a global setting, just to show that they _can_ be reasonable as well, although I think a lot less often than locals, so ignore that.) I think it’s pretty reasonable that the local variable has the same name as the selector key/keyword. If I ask “why do I have to repeat myself with genre=genre or 'genre': genre”, what’s the answer?

If I have 38 locals for all 38 selectors in the API—or, worse, a dynamically-chosen subset of them—then “get rid of those locals” is almost surely the answer, but with just 1? Probably not. And maybe 3 or 4 is reasonable too—a function that select by genre, subgenre, and mood seems like a reasonable thing. (If it isn’t… well, then I was stupid to pick an application area I haven’t done much work in… but you definitely don’t want to just select subgenre without genre in many music APIs, because your user rarely wants to hear both hardcore punk and hardcore techno.) And it’s clearly not an accident that the local and the selector have the same name. So, I think that case is real, and not dismissible.

I'm not saying it never comes up in well designed code -- it sure does, but if there's a LOT of that, then maybe some refactoring is in order.

Yes. And now that you point that out, thinking of how many people go to StackOverflow and python-list and so on looking for help with exactly that antipattern when they shouldn’t be doing it in the first place, there is definitely a risk that making this syntax easier could be an antipattern magnet. So, it’s not just whether the cases with 4 locals are important enough to overcome the cost of making Python syntax more complicated; the benefit has to _also_ overcome the cost of being a potential antipattern magnet. For me, this proposal is right on the border of being worth it (and I’m not sure which side it falls on), so that could be enough to change the answer, so… good thing you brought it up.

But I don’t think it eliminates the rationale for the proposal, or even the rationale for using it with JSON-related stuff in particular.