Hacker News new | ask | show | jobs
by BoumTAC 975 days ago
What I would love to see in a future version of python is being able to do `user["email"]` or `user.email` independently of the reason. Sometimes both work, sometimes only one of the two and an error in throw for the other one. I don't care why, I just want it to work, it's such a basic feature.

Something even crazier would be to have an equivalent of `console.log` in python. It would be an amazing feature but I think I'm the only one wanting it. I know I can use `print` or different logger. But it's a lot more complicated to use and the output is a lot less navigable than in javascript. PHP also has `var_dump`. But we don't have any equivalent in python.

8 comments

> What I would love to see in a future version of python is being able to do `user["email"]` or `user.email` independently of the reason. Sometimes both work, sometimes only one of the two and an error in throw for the other one. I don't care why, I just want it to work, it's such a basic feature.

It's an absolutely terrible idea and I'm thankful that there's so little chance it'll ever happen. I don't want random objects to become mappings, nor do I want mapping entries and their attributes to conflict. Javascript is a bad language and this is one of its worst features.

> Something even crazier would be to have an equivalent of `console.log` in python. It would be an amazing feature but I think I'm the only one wanting it. I know I can use `print` or different logger. But it's a lot more complicated to use and the output is a lot less navigable than in javascript.

You... can just call `logging.whatever()`, after having called `logging.basicConfig()` to set up your basic config?

I fail to see how that would change anything to navigability. `console.log` is not inherently navigable, it's the browser's console UI which provides some navigability.

In terms of programming language construction making `x.y` and `x["y"]` equivalent looks appealing and, admittedly, cute but there are some problems:

* For new languages: It's not generic enough since there is no equivalent of `x[t]` if t is of a non-string type. E.g. there is no way to express `x[(1,2,3)]` or `x[3]` or `x[frozenset({1, 2, "foo"})]` this way.

* For existing languages like Python: this would be a breaking change since things that can do `x.y` and `x[t]` are structurally different in Python so they're typed differently. One are called "mappings" and the other are "objects", they're completely different things. Hence, you'll get cases where `x["foo"] == 5` but `x.foo == 4` so this will for sure break some programs. Too much pain for no gain.

I will admit to implementing `__getattr__` and `__setattr__` in such a way that they mimic object properties in dictionaries, for specific cases. In general, the threshold for doing so should be IMHO fairly high. In my case, - they are data-heavy classes but not @dataclass classes, and - there's enough attribute access that the `["` and `"]` become visually distracting, and - there are nested structures, so so you can write `x.foo.bar.baz` instead of `x["foo"]["bar"]["baz"]` This is especially useful, in our case, in a system that intakes a lot of JSON with a LOT of nested dictionaries.
Don't you think `x["foo"] == 5` but `x.foo == 4` is a hell of a lot confusing ?

Don't you think it should not be possible to have such a thing ? To me it's so prone to error and there is absolutely too much gain to fix this.

> Don't you think `x["foo"] == 5` but `x.foo == 4` is a hell of a lot confusing ?

No, having used lots of OO languages before JS and its “objects are dictionaries are arrays and member access and string indexing and integer indexing are all equivalent and can be used interchangeably, except you can't use member access where the key isn't a valid identifier” approach, which I find more confusing and error prone (though I’ve since had to use JS enough to be proficient in that, too.)

Indexing an object as an indexable collection and accessing a member (field, property, or method) of the object are fundamentally different things, and having a collection item with a particular string index isn’t the same thing as an object member with a similar identifier name.

This use of objects as also quasi-associative-arrays is so broken that JS’s actual associative-array type (Map) can’t use indexing notation because of it, and has to use .get() and .set() instead, unlike the associative array types of most other dynamic languages (and several statically-typed OO languages).

The JS way is less bad as a type-specific behavior (e.g., Ruby ActiveSupport’s HashWithIndifferentAccess), though.

> Don't you think `x["foo"] == 5` but `x.foo == 4` is a hell of a lot confusing ?

No. They're different notations; one means `x.__getitem__('foo')` and the other means `x.__getattribute__('foo')`. Why should they be the same? It isn't confusing that `5-4 == 1` but `5+4 == 9`, after all.

If we assume that the dict class was enhanced with your proposed equivalence, would you want `d['items']` to be the function `d.items`? Would that not make 'items' a forbidden key?

No, there is no confusion here at all (for a Python developer). I would consider it a code smell though as the whole problem is completely avoidable by better naming.

By the way, there is a vulnerability (Prototype Pollution) that is only possible due to this behaviour in JS: https://portswigger.net/web-security/prototype-pollution

You can't confuse keys in a hash table with properties of objects. It's not confusing, it's a useful distinction.

Most languages that have both objects and associative arrays (so not Lua or JavaScript) make this distinction.

It's not particularly error prone. You're simply refusing ("don't care why") to learn how to use the tool you're given.

user dictionnary => user["email"]

user instance from a django model => user.email

It is error prone. You're simply refusing to see an aberration.

That's how the language works, but it doesn't mean it's intuitive and easy to understand especially for a language known for being easy to use and understand.

It's not how the language "works", it's what the language offers.

When all you have is a hash table, or when all you have is an object, you get to refer to keys and properties uniformly - because they are the same thing. When you have both, you refer to them differently - because they're different. That's it. There are some languages where objects and hash tables use the same syntax for access even though they're different things, but... you probably never used any of them, and certainly none of them is in the Top 20 on TIOBE.

I'll kick the venerable HN guidelines aside for a second and mention this: you're being heavily downvoted, all your comments in this thread are in various shades of gray, and many people offer many different arguments as to why you're wrong. Yet, you're undaunted and continue posting - I don't want to break the guidelines that much, but honestly, it reads like trolling. You don't engage with the arguments, you're just repeating the same thesis over and over again, without citing evidence. Why?

In pandas for example that can happen often: `df["count"] == 5` and `df.count == 5` are logically different expressions that will give different answers
So how can d.update() coexist with d["update"]? Updating a dictionary vs examining a dictionary that has a key "update"?
Try This:

  x = {"users": [12,21,54], "items": [1,2,3]}  # group owned items
  >>> x["items"]
  [1,2,3]
  >>> x.items
  <built-in method items of dict object at 0x00>
> PHP also has `var_dump`. But we don't have any equivalent in python.

Maybe pprint is for you:

    from pprint import pprint
    # usage, but can do more as well and has more config stuff if needed
    pprint({'a':'b'})

 https://docs.python.org/3/library/pprint.html
`vars` as well: https://docs.python.org/3/library/functions.html#vars

Although it'd be nice if it worked with slotted classes instead of just blowing up.

Dataclasses have asdict which work on both, but it's annoying there's nothing for regular classes.

There are a bunch of options for getting user["email"] and user.email to do the same thing:

1. Write a class with custom __getitem__ and __getattr__ methods

2. Use a template system like Django or Jinja that implements this in certain situations

3. Use a library like https://pypi.org/project/python-box/

I think what you are looking for as an equivalent to the JS `console.log`/PHP `var_dump` feature set is available in f-string formats.

There's not one perfect format to rule them all but the "=" "self-documenting" debug format such as `f"{some_obj=}"` probably gives you a good starting point for what you are looking for most of the time. Sometimes you still want the `str()` or `repr()` representation specifically and would want something like `f"{some_obj=!s}"` or `f"{some_obj=!r}"` respectively. In some cases objects you want pretty-printed to a console might have custom `__format__()` representations as well and it might be something like `f"{some_obj=:some-custom-format}"`.

It's obviously all still differently useful than JS having a full object browser embedded in most common consoles today, but there is interesting power to explore in f-string formats if you need quick and dirty logs of object states.

https://docs.python.org/3/whatsnew/3.8.html#bpo-36817-whatsn...

If you want to write javascript, use javascript. There are ways to get what you're asking for depending on your use case. types.SimpleNamespace in the standard library provides one approach.

"There should be one-- and preferably only one --obvious way to do it." (zen of python)

I do agree that python logging is a weak point. It is too easy to do it wrong -- particularly when you are a few modules deep.

I really like Python and I love using it. But I think some improvements could be done on such "basic feature".

> "There should be one-- and preferably only one --obvious way to do it." (zen of python)

Look at all the other possibilities to do `f"hello {name}`. That are more than one obvious way to do it.

A dict_to_object function would be a good little intermediate exercise.