On Mon, Apr 20, 2020 at 3:13 PM Andrew Barnert <abarn...@yahoo.com> wrote:
> Sure, it’s a declarative format, it’s just that often it’s intended to be
understood as representing an object graph.

I"m not sure the point here -- I was not getting onto detail nor
expalingnoi myself well, but I think there are (kind of) three ways to
"name" just one piece of data that came from a bunch of JSON:

- a key, as in a dict `data['this']`
- an attribute of an object: `data.this`
- a local variable: `this`

what I was getting at is that there may be a fine line between the dsta
version and the object version, but that can go between those easily
without typing all the names. It's only when you have it in a local
variable that this whole idea starts to matter.

> 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.

Of course not -- that was just the most trivial example. but it's also
trivial to unpack a whole graph, as long as you only want the standard
types that JSON can store. Even if you want custom types, you can still
build those from a dict of teh standrad tyupes.

And I've written (as have many others, I"m sure) code that does just that.

> 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.

sure. But what I'm suggesting is that even if you have a complex object
graph, you should write (or use a library)  code to do that unpacking for
you rather than, say:

blob = json.load(...)
people = []
for person in blob['people']
     name = person['name']
     phone_number = person['phone_number']
     # a bunch more
     people.append(Person(name=name,
                          phone_number=phone_number,
                          ...
                          ))

When you could write:

blob = json.load(...)
people = [Person(**person) for person in blob['people']]

Granted, I hope no one would write it quite that badly, but the point is
that the proposal in hand is only helpful if the information is in local
variables, and this kind of use case, that's probably not a good way to
structure the code.

>> Apologies for my overly-fragmentary toy example.

well, better than my no example :-) -- but I wasn't referring to any
particular example, I meant it generally -- again, this proposal is about
using local variables to fill in function calls (or dict construction) with
the same names, and I'm suggesting that if that is "data", then it probably
shouldn't be in local variables anyway. And if it it came from JSON (or
some such), then it took effort to put it in local variables ...

> 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]

well, the query params and the result are not really the same, on the data
vs code continuum. But...

> 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.)

Agreed. I do think that if this is going to happen at all, it should be a
dict display feature (which would help with both "data" and "code"), not a
function calling feature.

> 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?

Sure, but treating the result as data does not mean you have to treat the
query as data as well, and you may have a query_params class that holds all
that anyway :-) -- and even if they are locals -- where did they come from?
(read from a config file? gotten from user input?)  if you were doing a
full on JSON API, they may not have ever had to be in variables in the
first place.

> 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—

right. but I don't think anyone is suggesting a language change for 1, or
even 3-4 names (maybe 4...)

> 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 wasn't trying to dismiss it.

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
anti-pattern when they shouldn’t be doing it in the first place ...

Right. I think the data vs code distinction is a tough one in Python (maybe
a tiny bit less than JS?) In more static languages, you can kind of decide
based on what a pain it is to write the code -- if it's a serious pain,
it's probably data :-)

> And if we go back a couple messages in this thread, I was suggesting that
the anti-pattern was not using the same names in calling and function
scope, but rather, using local names, when it really should be data: in a
dict, or even a special object.

> 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,

Good point.

> 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.

Nor do I. However, as I think about it, where this may make the most sense
might be for cases when you're making a complex call that has SOME
arguments in the local namespace, with the some with other names, and some
specified as literals. That's pretty common pattern in the call to setup()
in setup.py files, for example.

-CHB





-- 
Christopher Barker, PhD

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/LLRMRFIO2SSA2GOQXUNJB5I2GOUOIBA6/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to