Re: [Python-ideas] Should Python have user-defined constants?
Hi. Just to respond to some peoples’ points… Bernardo asked 'This would be "constant" as in Java constants right? Referential constants, so that if an object were mutable you would still be able to change its internal state.’ Um, I’m not entirely sure what you mean, since I’ve never used Java. But if it means what I think it means, I would be INCLINED to say no. If, for example, I saw code like this: x = [9, 5, 7, 1] let y = x y[0] = 42 print(y) … I would expect that code to raise an error, not print “[42, 5, 7, 1]”, since you’re trying to modify the “constant” y in place, which… well, would kind of go against the idea of y being a constant, I thought (unless I’m misunderstanding what you meant by “object", since everything in Python’s an object). However, I might change my opinion if I heard a good reason why Java-like constants would be better. Serhiy asked, in relation to constants, “To do what? What problem do you need to solve?”. No problem in particular, to be honest. I just thought they’d be nice since they’d increase confidence that my variables-intended-to-be-constants wouldn’t get reassigned, and just to increase readability/explicitness to other programmers.
On 21 Nov 2017, at 20:08, python-ideas-request@python.org wrote:
Send Python-ideas mailing list submissions to python-ideas@python.org
To subscribe or unsubscribe via the World Wide Web, visit https://mail.python.org/mailman/listinfo/python-ideas or, via email, send a message with subject or body 'help' to python-ideas-request@python.org
You can reach the person managing the list at python-ideas-owner@python.org
When replying, please edit your Subject line so it is more specific than "Re: Contents of Python-ideas digest..."
Today's Topics:
1. Re: Should Python have user-defined constants? (Steven D'Aprano) 2. Re: Should Python have user-defined constants? (St?fane Fermigier) 3. Re: Should Python have user-defined constants? (Bernardo Sulzbach) 4. Star assignment in iterator way? (Kirill Balunov) 5. Re: Star assignment in iterator way? (Serhiy Storchaka)
----------------------------------------------------------------------
Message: 1 Date: Tue, 21 Nov 2017 19:13:42 +1100 From: Steven D'Aprano <steve@pearwood.info> To: python-ideas@python.org Subject: Re: [Python-ideas] Should Python have user-defined constants? Message-ID: <20171121081342.GK19802@ando.pearwood.info> Content-Type: text/plain; charset=us-ascii
On Tue, Nov 21, 2017 at 02:38:45AM -0500, Joseph Jevnik wrote:
How is that different from "pi = 3.14"?
pi = 3.14 pi = 5 print(pi) # prints 5
let pi = 3.14 pi = 5 # raises an exception
-- Steve
------------------------------
Message: 2 Date: Tue, 21 Nov 2017 09:26:17 +0100 From: St?fane Fermigier <sf@fermigier.com> To: Python-Ideas <python-ideas@python.org> Subject: Re: [Python-ideas] Should Python have user-defined constants? Message-ID: <CABuJJj5-ZhuWTBns8rm8jVWPaOGq08MM+X0ryQp1z7ogQFsHvw@mail.gmail.com> Content-Type: text/plain; charset="utf-8"
Javascript (ES6) has 'let' and 'const' for mutable and constant variables, respectively.
When programming in JS, I find myself aggressively aiming for as little 'let' and as much 'const' as reasonably possible, since reasoning about constants is much easier than about variables. In this context, 'const' is used as a marker, a runtime checker, and, with proper tooling (IDE or linter), a coding-time reminder to differentiate between these two fundamental behaviours.
I'm not +1 on introducing this idea to Python yet, but not -1 either - this deserves some discussion, if this has not been discussed already.
Cheers,
S.
On Tue, Nov 21, 2017 at 9:13 AM, Steven D'Aprano <steve@pearwood.info> wrote:
On Tue, Nov 21, 2017 at 02:38:45AM -0500, Joseph Jevnik wrote:
How is that different from "pi = 3.14"?
pi = 3.14 pi = 5 print(pi) # prints 5
let pi = 3.14 pi = 5 # raises an exception
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Stefane Fermigier - http://fermigier.com/ - http://twitter.com/sfermigier - http://linkedin.com/in/sfermigier Founder & CEO, Abilian - Enterprise Social Software - http://www.abilian.com/ Chairman, Free&OSS Group / Systematic Cluster - http://www.gt-logiciel-libre.org/ Co-Chairman, National Council for Free & Open Source Software (CNLL) - http://cnll.fr/ Founder & Organiser, PyData Paris - http://pydata.fr/ --- ?You never change things by ?ghting the existing reality. To change something, build a new model that makes the existing model obsolete.? ? R. Buckminster Fuller
21.11.17 11:34, Saeed Baig пише:
Serhiy asked, in relation to constants, “To do what? What problem do you need to solve?”. No problem in particular, to be honest. I just thought they’d be nice since they’d increase confidence that my variables-intended-to-be-constants wouldn’t get reassigned, and just to increase readability/explicitness to other programmers.
For increasing readability/explicitness you can use a name convention (UPPER_CASE), comments and documentation. As for increasing confidence that your variables-intended-to-be-constants wouldn’t get reassigned, you can use read-only properties. This is a tricky to implement read-only properties for modules, but see PEP 562 which should help to do this. Please don't use GIGANTIC font in your messages. And please don't create a new thread when answer. And *please* don't include unrelated text in your messages.
On Tue, Nov 21, 2017 at 11:03 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
21.11.17 11:34, Saeed Baig пише:
Serhiy asked, in relation to constants, “To do what? What problem do you need to solve?”. No problem in particular, to be honest. I just thought they’d be nice since they’d increase confidence that my variables-intended-to-be-constants wouldn’t get reassigned, and just to increase readability/explicitness to other programmers.
For increasing readability/explicitness you can use a name convention (UPPER_CASE), comments and documentation.
UPPER_CASE variable names are a reasonable convention for global constants. I wouldn't like to read something like: for i in range(0, 100): FOO = f(i) BAR = g(i, FOO) do_something_with(BAR) but rather: for i in range(0, 100): const foo = f(i) const bar = g(i, foo) do_something_with(bar) or: for i in range(0, 100): let foo = f(i) let bar = g(i, foo) do_something_with(bar) If we move on with this idea (which is a long shot of course), choosing between 'let' and 'const' is a matter of taste and being consistent with other languages. As for increasing confidence that your variables-intended-to-be-constants
wouldn’t get reassigned, you can use read-only properties. This is a tricky to implement read-only properties for modules, but see PEP 562 which should help to do this.
Please don't use GIGANTIC font in your messages.
+1. S.
And please don't create a new thread when answer.
And *please* don't include unrelated text in your messages.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Stefane Fermigier - http://fermigier.com/ - http://twitter.com/sfermigier - http://linkedin.com/in/sfermigier Founder & CEO, Abilian - Enterprise Social Software - http://www.abilian.com/ Chairman, Free&OSS Group / Systematic Cluster - http://www.gt-logiciel-libre.org/ Co-Chairman, National Council for Free & Open Source Software (CNLL) - http://cnll.fr/ Founder & Organiser, PyData Paris - http://pydata.fr/ --- “You never change things by fighting the existing reality. To change something, build a new model that makes the existing model obsolete.” — R. Buckminster Fuller
On Nov 21, 2017, at 5:40 AM, Stéfane Fermigier <sf@fermigier.com> wrote:
for i in range(0, 100): const foo = f(i) const bar = g(i, foo) do_something_with(bar)
This wouldn’t work, since a for loop doesn’t introduce a new scope for variables, and allowing a constant to be rebound in the same scope would somewhat defeat the purpose.
On Tue, Nov 21, 2017 at 08:34:31PM +1100, Saeed Baig wrote:
Hi. Just to respond to some peoples’ points… Bernardo asked 'This would be "constant" as in Java constants right? Referential constants, so that if an object were mutable you would still be able to change its internal state.’ Um, I’m not entirely sure what you mean, since I’ve never used Java. But if it means what I think it means, I would be INCLINED to say no. If, for example, I saw code like this:
x = [9, 5, 7, 1] let y = x y[0] = 42 print(y)
… I would expect that code to raise an error, not print “[42, 5, 7, 1]”, since you’re trying to modify the “constant” y in place, which… well, would kind of go against the idea of y being a constant, I thought (unless I’m misunderstanding what you meant by “object", since everything in Python’s an object).
That depends on what we mean by constant. If you mean that the *values* are immutable and cannot be changed, then Python already has constants. The object 99 (an int) is always equal to 99, you cannot change its value. Operations on it return a new object, not the same one modified. The same applies to strings and tuples. For example, consider the difference between a *mutable* object like a list, and an immutable one like a tuple: a = b = [1, 2] a += [999] print(b) # prints [1, 2, 999] Clearly the value [1, 2] is not "constant" in the sense of being immutable, unlike tuples: a = b = (1, 2) a += (999,) print(b) # prints (1, 2) So if that's what you mean by "constant", then most Python values (strings, ints, floats, bools, tuples, frozensets...) are already constants -- and the ones which aren't, we *want* to be mutable. But what Bernardo refers to, "referential constants", are more like constants in languages like C, Pascal, Java, etc. The value itself may or may not be mutable, or immutable, but the *name binding* is fixed. That is, once you have defined a constant, e.g. in Pascal you use a const declaration: const a = 1; in other languages you may use a "let": let a = 1 a = 2 # an error from this point on, the variable name "a" always refers to the same value. In this case, that value will be 1, which is an immutable object but it could be a mutable object like a list: let b = [] b.append(999) # modifies the object # but the name still refers to the same object b = [1, 2, 3] # an error The language we might choose here is to say that constants are "bind-once". That is, we can bind an object to the name once: let c = some(expression) but any subsequent attempts to re-bind or unbind the name should fail: c = something_else # fails del c # also fails That is, I think, what Bernardo means by referential constants, and that's what I mean when I talk about constants in Python. The question of mutability and immutability is orthogonal to the concept of being able to re-bind a name. They refer to two different concepts: - if the value is a fixed object, unable to be modified, we call the object "immutable"; otherwise the object is mutable; - if the *name* is fixed to the same object, unable to be changed to a different object, we might call the *name* a constant; otherwise the name is a variable. Because they are independent concepts, a language might support any combination of: - constant name with immutable value; - constant name with mutable value; - variable name with immutable value; - variable name with mutable value. Constants in Pascal and C are both constant *names* and immutable *values*. The closest they have to constant names with mutable values would be if you set a constant to a pointer (if the compiler supports that). In Pascal, if it were allowed, that might look something like: const a = ^x; {a is set to the address of x} In C, it might be something similar to: const int *a = &x; (I think). In either case, if the compiler supported this, you wouldn't be able to modify the pointer a itself, but you could modify the area of memory that a points to (that is, x). I don't think there is anything interesting about "constants" which are nothing more than immutable objects. We already have that. The interesting (but is it useful?) concept is constant identifiers which cannot be re-bound or re-assigned once they are set the first time. P.S. Please do not reply to digests without trimming all the excessive quoted emails that are not relevant to the discussion. -- Steve
On Tue, Nov 21, 2017 at 11:40:14AM +0100, Stéfane Fermigier wrote:
for i in range(0, 100): const foo = f(i) const bar = g(i, foo) do_something_with(bar)
I wouldn't expect that to work. You are binding some value f(0) to the constant name "foo" on the first loop, then on the second loop you try to rebind a new value to the same name "foo". I would expect that to be an error. -- Steve
Are there any 3rd party tools that already implement this? By my understanding, type annotations started as 3rd party tools that became popular and so are now incorporated into the language proper. I could see a 'const' syntax making its way into the language that way, but not directly. Certainly this idea has been hashed out multiple times before.
On Tue, Nov 21, 2017 at 3:31 AM, Steven D'Aprano <steve@pearwood.info> wrote: <snip>
The interesting (but is it useful?) concept is constant identifiers which cannot be re-bound or re-assigned once they are set the first time.
This would actually be a substantial shift in what Python is about -- currently the concept of type is all about values, not names -- there are various rules about scope, and they can be adjusted with global and nonlocal, but it's only about scope -- a name is still a name, and can be bound to any type of object, etc. static languages, on the other hand often (always) assign type to the name itself -- so adding constants and the like makes sense. But changing the property of a name (this name can not be rebound) is a big shift in what names are about in Python -- and I don't think a good one. And what's the use-case, really? beyond the use case for all sorts of static typing... in case it's not obvious: -1 -CHB -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov
On Tue, Nov 21, 2017 at 6:34 PM, Chris Barker <chris.barker@noaa.gov> wrote:
And what's the use-case, really? beyond the use case for all sorts of static typing...
Javascript is dynamically typed, and has 'const' variable declaration which were added in ES6. Static typing is not the only use case. Discouraging reuse of variables of favouring functional style over imperative is a strong use case for 'const' in JS. But I agree that the scoping rules are different in Javascript, and this argument is much less convincing in Python. S. -- Stefane Fermigier - http://fermigier.com/ - http://twitter.com/sfermigier - http://linkedin.com/in/sfermigier Founder & CEO, Abilian - Enterprise Social Software - http://www.abilian.com/ Chairman, Free&OSS Group / Systematic Cluster - http://www.gt-logiciel-libre.org/ Co-Chairman, National Council for Free & Open Source Software (CNLL) - http://cnll.fr/ Founder & Organiser, PyData Paris - http://pydata.fr/ --- “You never change things by fighting the existing reality. To change something, build a new model that makes the existing model obsolete.” — R. Buckminster Fuller
בתאריך יום ג׳, 21 בנוב׳ 2017, 19:36, מאת Chris Barker < chris.barker@noaa.gov>:
...
And what's the use-case, really? beyond the use case for all sorts of
static typing...
I don't understand the question. The use case was explained before - people want to have better ways to reason about their programs. Statically. Why dismiss it as a non-usecase? It's helpful for both tools and humans. It was mentioned that there are related conventions in Python. Good point. But they are not as strong as the "convention" that tuples and strings cannot change, and this is useful in reasoning about code - in understanding the program, essentially. When I read "final int x = 5;" in Java, I don't have to think about it anymore - it's 5. When I read "X = 5" in Python, it might be a constant, but it might also be a misnomer, or something that used to be a constant, or a class reassigned. I still have to watch whether it will be changed, intentionally or accidentally, by this or other module. It does not even communicate intention in an unambiguous way. In my opinion, the inability to control mutation in Python is the biggest readability issue of the language (thank god monkey-patching is discouraged). I see why the proposal can be rejected, but I don't understand the "you don't gain anything" responses. There are significant gains there. Elazar
Steven D'Aprano wrote:
for i in range(0, 100): const foo = f(i) const bar = g(i, foo) do_something_with(bar)
You are binding some value f(0) to the constant name "foo" on the first loop, then on the second loop you try to rebind a new value to the same name "foo".
It could be made to kind of work if you were allowed to re-execute the same let statement, but not assign to the name with another statement in the same function. -- Greg
Steven D'Aprano wrote:
In C, it might be something similar to:
const int *a = &x;
Not quite, that makes whatever a points to constant, not a itself. But you can do this: typedef int *ip; int x, y; const ip p = &x; void f(void) { *p = 42; /* ok */ p = &y; /* not ok */ } Compiling this gives: % gcc -c constptr.c constptr.c: In function ‘f’: constptr.c:7: error: assignment of read-only variable ‘p’ BTW, this shows that gcc thinks of const-declared things as "read-only variables" rather than constants. Which is a bit of a silly term when you think about it! -- Greg
On 21.11.2017 19:05, אלעזר wrote:
I don't understand the question. The use case was explained before - people want to have better ways to reason about their programs. Statically. Why dismiss it as a non-usecase? It's helpful for both tools and humans.
Then type annotations are for them. But please don't clutter the core language with this.
When I read "final int x = 5;" in Java, I don't have to think about it anymore - it's 5. When I read "X = 5" in Python, it might be a constant, but it might also be a misnomer, or something that used to be a constant, or a class reassigned.
What about using real-world names instead of X? That could make the intentions crystal clear if you ask me. Cheers, Sven PS: it seems to me that this resembles the typing discussions here. Using non-sensical variable names to explain why type annotations are sorely needed.
On Tue, Nov 21, 2017 at 09:34:33AM -0800, Chris Barker wrote:
On Tue, Nov 21, 2017 at 3:31 AM, Steven D'Aprano <steve@pearwood.info> wrote:
The interesting (but is it useful?) concept is constant identifiers which cannot be re-bound or re-assigned once they are set the first time.
This would actually be a substantial shift in what Python is about -- currently the concept of type is all about values, not names
I didn't say anything about types. My comments were about names and values (objects): - Mutability is a property of values (objects): some objects are mutable, and other objects are not. - Constantness is a property of names or other identifiers: some names can only be bound once ("constants"), other names can be bound and rebound at will ("variables"). Types are literally irrelevant to this, except in the sense that in many languages, including Python, the distinction between mutable and immutable is usually controlled by the type of object. But that's not fundamental to the concept: we could, if we wanted, decouple the two.
-- there are various rules about scope, and they can be adjusted with global and nonlocal, but it's only about scope -- a name is still a name, and can be bound to any type of object, etc.
Sure, that's the way Python is now. But that's not fundamental to the concepts. Other languages allow other distinctions between names, not just scope: for instance, names can be static (they keep their value between function invocations), they can be read-only some languages allow you to specify whether they are kept in RAM or in a register.
static languages, on the other hand often (always) assign type to the name itself -- so adding constants and the like makes sense.
Constantness is orthogonal to the concept of static typing of variables.
But changing the property of a name (this name can not be rebound) is a big shift in what names are about in Python -- and I don't think a good one.
It is a big shift, although not that big since we already have read-only properties. This would "merely" (hah!) extend that concept to other namespaces apart from instances. I'm undecided as to whether the benefit would justify the cost. -- Steve
On Wed, Nov 22, 2017 at 11:27 AM, Steven D'Aprano <steve@pearwood.info> wrote:
On Tue, Nov 21, 2017 at 09:34:33AM -0800, Chris Barker wrote:
On Tue, Nov 21, 2017 at 3:31 AM, Steven D'Aprano <steve@pearwood.info> wrote:
The interesting (but is it useful?) concept is constant identifiers which cannot be re-bound or re-assigned once they are set the first time.
This would actually be a substantial shift in what Python is about -- currently the concept of type is all about values, not names
I didn't say anything about types. My comments were about names and values (objects):
- Mutability is a property of values (objects): some objects are mutable, and other objects are not.
- Constantness is a property of names or other identifiers: some names can only be bound once ("constants"), other names can be bound and rebound at will ("variables").
Types are literally irrelevant to this, except in the sense that in many languages, including Python, the distinction between mutable and immutable is usually controlled by the type of object. But that's not fundamental to the concept: we could, if we wanted, decouple the two.
There's a good reason for that - the type of an object determines what operations make sense (you can add two numbers together, but you can't add two open sockets), and mutability impacts the validity of operations. Can you give an example of where it would make sense to decouple mutability from object type? Everything I can think of in Python works by creating a separate type (eg frozenset for set), thus allowing new operations (eg hashing). The only way I can think of to decouple them would be to have a single type with all the operations available, and then raise exceptions where some operations aren't valid: class Foo: def __hash__(self): if self.mutable: raise TypeError return some_hash def __iadd__(self, other): if not self.mutable: return self + other # perform an in-place addition return self def __setitem__(self, key): if not self.mutable: raise TypeError # alter self This makes it hard to reason about the code - particularly in the case of __iadd__, where it'll sometimes behave like a list's += and sometimes like an int's. Where would you, preferably with some sort of real-world example, actually make use of this sort of thing? ChrisA
On Wed, Nov 22, 2017 at 11:47:28AM +1100, Chris Angelico wrote:
On Wed, Nov 22, 2017 at 11:27 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Types are literally irrelevant to this, except in the sense that in many languages, including Python, the distinction between mutable and immutable is usually controlled by the type of object. But that's not fundamental to the concept: we could, if we wanted, decouple the two.
There's a good reason for that - the type of an object determines what operations make sense (you can add two numbers together, but you can't add two open sockets), and mutability impacts the validity of operations. Can you give an example of where it would make sense to decouple mutability from object type?
I didn't say we should, or that I would, only that we could if we wanted to. But for the sake of the hypothetical argument, being able to freeze an object may be useful, as opposed to copying it into a new, frozen object. To make the difference clear: a = b = something() a = freeze(a) b would still be mutable; but: a = b = something() freeze(a) now b is immutable, since it is the same frozen object as a. But possibly even more interesting might be to have the concept of code permissions, so that you can grant software components either read/write or read-only permission on values you pass to them. (Immutable views could possibly do the same job.) At the moment every time you pass a list to a library function, you risk it modifying the list without your knowledge. In general that's more of a theoretical risk than an actual risk (we generally know which library functions mutate their arguments), but there may be circumstances where you'd like to pass a mutable object to (let's say) a callback function, or a thread, and trust that it can't modify the object at all. At the moment, the only obvious alternative is to make an arbitrarily expensive deepcopy of the object each time you pass it. Or just hope that the called function doesn't mess with your data. -- Steve
On Wed, Nov 22, 2017 at 12:51 PM, Steven D'Aprano <steve@pearwood.info> wrote:
On Wed, Nov 22, 2017 at 11:47:28AM +1100, Chris Angelico wrote:
On Wed, Nov 22, 2017 at 11:27 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Types are literally irrelevant to this, except in the sense that in many languages, including Python, the distinction between mutable and immutable is usually controlled by the type of object. But that's not fundamental to the concept: we could, if we wanted, decouple the two.
There's a good reason for that - the type of an object determines what operations make sense (you can add two numbers together, but you can't add two open sockets), and mutability impacts the validity of operations. Can you give an example of where it would make sense to decouple mutability from object type?
I didn't say we should, or that I would, only that we could if we wanted to. But for the sake of the hypothetical argument, being able to freeze an object may be useful, as opposed to copying it into a new, frozen object.
Ah okay. Yeah, it's certainly plausible in theory, but I've never actually wanted it. ChrisA
participants (11)
-
brent bejot
-
Chris Angelico
-
Chris Barker
-
Clint Hepner
-
Greg Ewing
-
Saeed Baig
-
Serhiy Storchaka
-
Steven D'Aprano
-
Stéfane Fermigier
-
Sven R. Kunze
-
אלעזר