[Python-ideas] Re: addition of "nameof" operator

2020-01-30 Thread Andrew Barnert via Python-ideas
On Jan 30, 2020, at 11:20, Johan Vergeer  wrote:
> 
> It is a couple of days later, but I managed to create a more expanded 
> proposal.
> 
> This proposal is about having a simple and consistent way of getting the name 
> of an object.
> Whether it is a class, type, function, method or variable or any other object.

I think this is glossing over something very important.

You really aren’t trying to get the name of an object at all, you’re trying to 
get the name of a _variable_.

After you write foo.bar = 2, the object in foo.bar is just the number 2, an 
object of type int that’s probably the same object that’s referenced in dozens 
of other places, both named and anonymous, in your program’s current state. It 
can’t possibly have the name “bar”.

What _can_ have the name “bar” is the variable that references that value. But 
that variable isn’t a thing that exists in Python at runtime.

A compile-time transformation could convert nameof(foo.bar) into “bar”. In 
fact, the compiler already pretty much has to do exactly that to emit the code 
for foo.bar, which is effectively 'push the object in too, then push the string 
“bar”, then do an attr lookup with those two things'.

So nameof cannot be a function. It has to be a special compiler-time operator. 
The code emitted for nameof(foo.bar) has to be effectively the same code 
emitted for the literal “foo”.

And this affects a whole lot of your other points below:

> ```python
> def my_factory(class_name: str):
>if class_name == "Foo":
>return Foo()
>if class_name == "Bar":
>return Bar()
> ```

The way you already do this today is usually to look up the class name in a 
dict—either directly, or as part of a namespace lookup (e.g., getattr(self, 
class_name)). Having nameof wouldn’t really help here.

However, it could help on the _caller_ side: you could call 
my_factory(nameof(Foo)). In this trivial case, there’s no reason for that, 
because if you have the name Foo you already have the class object Foo and 
don’t need a factory in the first place. But there might be less trivial cases 
where it might be useful.

> I know this is a very simple example that can be solved with the example 
> below, but it would be nice if we can do the same with methods, functions and 
> attributes.

But you can already write that factory that way. Methods are attributes, and 
both can be looked up with getattr. Functions are attributes of the module 
object, and members of globals, so they can be looked up either way. You don’t 
need nameof for this kind of dynamic lookup, and it doesn’t help to add it. You 
only need nameof got static (compile-time) lookup, to serve the purposes from 
your initial paragraph, like making sure the name gets caught by refactoring 
tools (which can be sure that nameof(Foo) must rename along with Foo, but can’t 
know whether the string literal “Foo” needs to change).

> ## Class names
> 
> When you want to get the name of a class, you should just use the name. So 
> `nameof(Foo)` becomes `"Foo"` and `nameof(type(foo))` also becomes `"Foo"`.
> The results are basically the same as `Foo.__name__` and `type(foo).__name__`.

So what happens here:

class Foo: pass
Bar = Foo
print(nameof(Bar))

If you compile nameof(Bar) into a call to a normal (runtime) nameof function 
that returns param.__name__, you’re going to print out Foo.

If you compile it to just Bar.__name__, you’re still going to print out Foo.

If you compile it to the string "Bar", you’re going to print out Bar. And I 
think that’s what you actually want here.

Meanwhile, I don’t see how nameof(type(foo)) can work. At compile time, we have 
no idea what the type of foo is. Consider this case:

foo = Foo() if spam else Bar()

Even if the compiler were smart enough to know that Foo and Bar are guaranteed 
to be names for the same object (which they actually might not be even after 
Bar = Foo—you can write a globals dict whose __setitem__ does something crazy… 
but ignore that), it still can’t know which name we used here without knowing 
the runtime value of spam, which it can’t possibly have.

As a side note, this implies that making nameof look like a function call (with 
parentheses around an argument) is misleading. Everything that looks like a 
function call in Python is actually compiled as a function call, and I don’t 
think you want to break that. And there’s no real reason to—Python already has 
spelled-out prefix operators like not, so why shouldn’t nameof be the same? (In 
C# things are different.)

> ## Attribute names
> 
> You should be able to get the name of an attribute. 
> 
> ```python
> class Foo:
>bar: str = "Hello"
> 
>def __init__(self):
>self.baz = "World"
> 
>def __str__(self):
>return f"{nameof(self.bar)}: {bar}, {nameof(self.baz): {baz}}"  # 
> Returns "bar: Hello, baz: World"
> 
> foo = Foo()
> 
> nameof(foo)  # Returns "foo"
> nameof(foo.bar) # Returns "bar"


If you compile nameof(foo.bar) t

[Python-ideas] Re: Allow spaces between string prefix and the string literal

2020-01-30 Thread Steven D'Aprano
On Thu, Jan 30, 2020 at 12:39:29AM +0300, Mikhail V wrote:
> I would like to see possibility to put spaces
> between the string prefix and the string literal
> so I could write e.g. like this:
> 
> print (f "x: {x}")
> 
> IMO it would help with legibility especially
> noticable with by proportional fonts.

If you are having problems with your source code being less legible and 
harder to read due to the use of proportional fonts, rather than asking 
to change the language, you should stop using proportional fonts for 
reading code.

Or at least choose a better font. If your chosen font makes f" and f' 
illegible, then you have chosen badly.


-- 
Steven
___
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/H6DSXFBIAREJBVB6ZVLP3CZK7RL46GHH/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Allow spaces between string prefix and the string literal

2020-01-30 Thread Brett Cannon
The problem is that's ambiguous to the grammar whether you truly mean 'f'
as a prefix or 'f' as a variable and just happened to type something wrong.
And then debugging that would be horrible. So even if the grammar to
support it, I'm -1 on the idea.

On Wed, Jan 29, 2020 at 1:41 PM Mikhail V  wrote:

> I would like to see possibility to put spaces
> between the string prefix and the string literal
> so I could write e.g. like this:
>
> print (f "x: {x}")
>
> IMO it would help with legibility especially
> noticable with by proportional fonts.
>
> Mikhail
> ___
> 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/OXR2HHUYZYXJXNOL4INTOAOPXJWWCZBA/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
___
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/FNN6LK4DKQDCKLSULINBW524MX2V7N3H/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: addition of "nameof" operator

2020-01-30 Thread Johan Vergeer
It is a couple of days later, but I managed to create a more expanded proposal.

This proposal is about having a simple and consistent way of getting the name 
of an object.
Whether it is a class, type, function, method or variable or any other object.

# Why?

## Usage in strings

The first situation this could be used is in strings (for example in 
`__repr__`, `__str__` and `print()`)
I've come across too many situations where the developer made a typo (which is 
usually the developer is a hurry or just having a bad day)
or in the case of a refactoring where the developer didn't rename the object in 
the string representation.

To be honest, I am a big fan of IDEs, like PyCharm, which have great 
refactoring tools, but it doesn't refactor strings.

## Usage in comparisons

Another situation is when you want to do a comparison. An example of this would 
be:

```python
def my_factory(class_name: str):
if class_name == "Foo":
return Foo()
if class_name == "Bar":
return Bar()
```

I know this is a very simple example that can be solved with the example below, 
but it would be nice if we can do the same with methods, functions and 
attributes.

```python
def my_factory(class_name: str):
if class_name == Foo.__name__:
return Foo()
if class_name == Bar.__name__:
return Bar()
```

## The Zen of Python
 
The Zen of Python has a couple of items that apply to this one in my opinion:
 
Beautiful is better than ugly.
Simple is better than complex.
Sparse is better than dense.
Readability counts.
**There should be one-- and preferably only one --obvious way to do it.**
Although that way may not be obvious at first unless you're Dutch.

# How?

>From the messages in this thread I concluded there is some confusion about 
>what I would like to achieve with this proposal.
In this part I will try to make it more clear what the behavior would be. For 
now I'll stick to the C# naming `nameof()`. 

## Class names

When you want to get the name of a class, you should just use the name. So 
`nameof(Foo)` becomes `"Foo"` and `nameof(type(foo))` also becomes `"Foo"`.
The results are basically the same as `Foo.__name__` and `type(foo).__name__`.

## Function names

When you want to get the name of a function, you would do the same you do with 
a class.

```python
def foo():
...

nameof(foo) #Returns 'foo'
```

This is (again) the same as `foo.__name__`

## Attribute names

You should be able to get the name of an attribute. 

```python
class Foo:
bar: str = "Hello"

def __init__(self):
self.baz = "World"

def __str__(self):
return f"{nameof(self.bar)}: {bar}, {nameof(self.baz): {baz}}"  # 
Returns "bar: Hello, baz: World"

foo = Foo()

nameof(foo)  # Returns "foo"
nameof(foo.bar) # Returns "bar"
```

As Chris Angelico suggested we can already use a special syntax for f-strings 
which is pretty cool, 
but it has the limitation that we have less control over the output.

```python
return f"{self.name=}"   # returns `self.name="something"`
```

## Invalid usage

There are also situations that will return an error.

```python
nameof(1)
nameof("foo")
nameof(["bar", "baz"])
```

## Stuff to think about

There are also situations that I haven't been able to make a decision about and 
I think these should be discussed further down the road.

```python
nameof(None)
nameof(type)
nameof(int)

_foo = Foo()
nameof(_foo) #Should this return "_foo" or "foo"?
...
```

## How should the interpreter handle it?

I think the interpreter should handle it as anything else that is passed to a 
function, 
so when the value passed to the `nameof()` function doesn't exist, an error 
should be thrown.

# How can this be built in the Python language (or a library)

Some people responded this will be very hard to implement because of the way 
Python is built.
To be honest, I don't know nearly enough about the internals of Python to make 
a solid statement on that.

I also don't know how the new f-string syntax `f"{self.name=}"` works under the 
hood, 
but it does give me the impression that implementing this proposal should be 
possible.

# What about `__name__`?

Most of the examples I wrote above can be solved by just using `__name__`, 
which solves most use cases, except for variable and attribute names, 
which cannot be resolved right now because objects don't have names.

So when we want to follow `There should be one-- and preferably only one 
--obvious way to do it.`
it would be better to use `__name__` on an object, but I believe that would 
cause breaking changes.

What I mean is:

```python
class Foo:
...

foo = Foo
print(foo.__name__) # Prints "foo"

foo = Foo()
print(foo.__name__) # Throws AttributeError: 'Foo' object has no attribute 
'__name__'
```
___
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