Introduce constants in Python (constant name binding)

I posted my previous idea regarding this on the mailing list. This idea is a little different. This idea suggests introducing constant name bindings. This is similar to const pointer in C/C++. Once a name has been assigned to a data we can change the data (if mutable) but we cannot change the name to point to a different data. The only way the data the constant points can get deallocated is if it goes out of scope, the program exits or the constant is manually `del` by the user. The proposed syntax is as follows, constant x = 10 constant y = ["List"] constant z: str = "Hi" Thanking you, With Regards

On Tue, May 25, 2021 at 9:55 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
https://docs.python.org/3/library/typing.html#typing.Final Already exists :) ChrisA

Yes I'm aware of that. That's for typing. I'm talking about implementing it in Python itself not Python typing.

On Tue, May 25, 2021 at 4:02 PM Ethan Furman <ethan@stoneleaf.us> wrote:
I mean, it's part of the standard library, so we could argue on personal definitions of 'core Python', but it would be more useful to work out exactly what aspects of typing.Final are lacking for you. Why would this constant annotation need to be more strongly enforced than type checks? What actual benefit would this provide? Steve --

We've already had all of this since long before typing.py existed. ALLCAPS means "this value should only be set once and not changed." _underscore means "this is not part of public API, and it may change in next version. __double_under means "this is SERIOUSLY not something you should muck with, it's likely to break stuff if you do." Python should never become a B&D language like Java. If a user violates the contracts provided by code they utilize, that's on them, not on the language. On Tue, May 25, 2021, 11:28 AM Stestagg <stestagg@gmail.com> wrote:

On Tue, May 25, 2021 at 11:37:06AM -0400, David Mertz wrote:
We've already had all of this since long before typing.py existed.
ALLCAPS means "this value should only be set once and not changed."
`math.pi` says hello. The existence of math.pi and the fact that probably nobody ever has introduced a bug into their code by carelessly assigning another value to it suggests strongly that the benefit of language enforced constants is not high. But then on the other hand, who would ever want to change the value of pi?
_underscore means "this is not part of public API, and it may change in next version.
Namedtuple and the sys module also say hello. Namedtuple reserves single underscore names to itself, since any regular name could be a field name. So namedtuples have five public APIs starting with underscores. https://docs.python.org/3/library/collections.html#collections.namedtuple The sys module has numerous documented and apparently public functions starting with underscores, apparently to indicate that they are, or may be, implementation-dependent, or "internal and specialized purposes only", or for no given reason at all. https://docs.python.org/3/library/sys.html The naming conventions in the sys module are very inconsistent. Other functions that are clearly implementation-dependent have regular names, and some underscore names are in common use. E.g. _getframe.
__double_under means "this is SERIOUSLY not something you should muck with, it's likely to break stuff if you do."
That's a convention I've never heard of before. What's the difference between "private" and "SERIOUSLY private, I REALLY mean it"? The convention I know for double-underscores is that *in classes only* a name with a leading double-underscore is name-mangled in order to avoid naming conflicts with subclasses. Its not really more private than single-underscore names because it is transformed into a single- underscore name: __spam --> _classname__spam
Python is not really a design by contract language. Types are strongly checked, at runtime, and you can't turn that off. We have no syntactic support for contracts. A few third-party libraries have attempted to provide pre- and post-condition checking, but without syntactic support they are awkward to use. Its hard to blame people for violating a contract that they don't know applies, isn't an actual contract, and isn't enforced. The problem with naming conventions is that not everyone knows the conventions. Not every Python programmer spends hours every day on Python forums, reading Stackoverflow, etc. What you call "a B&D language like Java", other people might call "safety rails and warning lights". -- Steve

On Tue, May 25, 2021 at 9:36 PM Steven D'Aprano <steve@pearwood.info> wrote:
ALLCAPS means "this value should only be set once and not changed."
`math.pi` says hello.
(U ⇒ O) ⊬ (¬U ⇒ ¬O) The existence of math.pi and the fact that probably nobody ever has
Well, rarely. I've once or twice carelessly tested or taught some iterative algorithm for generating pi and made the error of overwriting the name. In this particular example, and also math.e and math.tau, the lowercase follows external conventions. From time to time other such mathy constants too. But the UPPERCASE constants are pretty darn common, pre-dating Python.
Yeah. There are a few exceptions. I'll note... perhaps *confess*... that even though I was literally sitting at the desk next to Raymond when he was thinking about the design of namedtuple, and even discussed it with him slightly, I had assumed for more years than I wish to mention that ._asdict and ._fields were not strictly promised because of their names.
In classes, it's name mangling. I've found that most people, or most code I've seen, treat module or function level `__double` names as implicitly similar. That's certainly what I do myself. If I don't want someone to rely on a name in future versions of some module, I'll use the single underscore. If I want to warn them that relying on it might cause bad things even right now, in this version, I'll use the double leading-underscore. Obviously it's not enforced at a language level. But it's a pretty good clue for other users.
It seems like a documentation question then. PEP 8 describes it as: - _single_leading_underscore: weak "internal use" indicator. E.g. from M import * does not import objects whose names start with an underscore. - __double_leading_underscore: when naming a class attribute, invokes name mangling - __double_leading_and_trailing_underscore__: "magic" objects or attributes that live in user-controlled namespaces. E.g. __init__, __import__ or __file__. Never invent such names; only use them as documented. It's not 100% explicit about double underscore meaning "I REALLY mean it" ... but it's kinda implied in the contrast with single underscore. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.

On Tue, May 25, 2021 at 04:27:41PM +0100, Stestagg wrote:
On Tue, May 25, 2021 at 4:02 PM Ethan Furman <ethan@stoneleaf.us> wrote:
The actual enforcement perhaps? Without mypy, which is not part of the standard library or core language, annotating something with typing has no more benefit than a naming convention -- and likely less, since naming conventions are visible every time you refer to the name, not just at the point of creation.
Why would this constant annotation need to be more strongly enforced than type checks? What actual benefit would this provide?
I guess that comes down to your position on how strongly you care about backwards compatibility for people who ignore your (not-so?) clear documentation. If you take the Microsoft position of trying very hard not to break the code of people even if they have used undocumented internals or clearly documented private areas, you might want the interpreter to do more to discourage those practices. If you take the Apple position of not giving a damn whose code you break, then you might not. -- Steve

It's still a Python feature even if it's not a language feature, it's well defined by PEP and any type checker wanting to implement type hinting to spec must include it. Further type hinting allows developers who want to use a Constant / Final feature get the benefit already from type hinting, whereas developers who want to stick to the pythonic philosophy of "we're all consenting adults here" can still mess with the code but know it's not supported at all. As far as I can see it's the best of both worlds. Could you please list in your proposal the real world benefits vs. the existing type hinting feature? On Tue, May 25, 2021 at 11:02 AM Ethan Furman <ethan@stoneleaf.us> wrote:

On 5/25/21 8:33 AM, Damian Shaw wrote:
To be clear, this is not my proposal, and I completely agree with David Mertz. I am -1 on the proposal. Nevertheless, the proposal is not satisfied by a stdlib module that is completely ignored by the Python runtime -- that was the only point I was trying to make. -- ~Ethan~

First it would seem useless or not necessary but soon this becomes clear. 1) It would help programmers debug their code easily and help them find out that the bug's not in the constant, that's for sure. 2) This would allow another branch of OOP programming to enter Python. Read-only member variables. There are Private members in Java and C++. But I believe they are not their to hide things. They are there to debug.

On Tue, May 25, 2021 at 4:41 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
A reasonable type checker will detect cases where typing.Final annotated bindings are reassigned. For example, with mypy: https://mypy.readthedocs.io/en/stable/final_attrs.html If mypy is not catching cases of rebinding, then that would likely be treated as a bug by the maintainers. If you want help with debugging code, then a type checker will help with that.
that's for sure. 2) This would allow another branch of OOP programming to enter Python. Read-only member variables.
see above, this is already provided by mypy There are Private members in Java and C++. But I believe they are not their
to hide things. They are there to debug.
There are private methods in python too, just not enforced by the runtime, instead enforced by the myriad developer/debug tooling out there in the form of linters/checkers.

I'm aware that Python is a "Consenting Adults" language but I think Python should provide this functionality at least. This idea doesn't actually make it like Java. Because why should we mess around with that member anyway. Sometimes we want to provide members that users must use but mustn't change. Well if the user does, then "crash"! Suppose we have filename member and we must access that but if we change it by any chance then what will happen is Python will not close the file instead it will create a new file leaving it in an undefined state - "Half written half not".

On Wed, May 26, 2021 at 2:00 AM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
I'm aware that Python is a "Consenting Adults" language but I think Python should provide this functionality at least. This idea doesn't actually make it like Java. Because why should we mess around with that member anyway. Sometimes we want to provide members that users must use but mustn't change. Well if the user does, then "crash"! Suppose we have filename member and we must access that but if we change it by any chance then what will happen is Python will not close the file instead it will create a new file leaving it in an undefined state - "Half written half not".
At best, Python could check this at run time, after it's too late. Type annotations and a tool like mypy can check it at, effectively, compilation time. All you have to do is run mypy on your code, and you'll know if that private member got assigned to. That's its point! What advantage would be gained by adding this functionality to the language interpreter, as opposed to being in the typing module and verified by an external tool? Do you really need your validation to be done later and in an unreliable way? There are four points at which a bug could be detected: 1) Editing 2) Deployment 3) Compilation (in Python's case, that happens on first import of the module) 4) Execution (an exception when you actually attempt the mutation) #1 is achieved by ALL_CAPS_NAMING_CONVENTION, because if anyone attempts to assign to it, it's obvious that something's wrong. That's the absolute earliest that you could possibly notice the bug, and *we already have that feature* built into our own brains. #2 would be where MyPy would catch the bug. Include a type checker pass in your pre-deployment checklist, possibly enforced by your pull request system, or verified automatically as part of CI. I'm not sure how you could catch this kind of thing at phase #3, but it might be possible. #4 is where you'd actually catch it, if your proposal were to be implemented. The mutation would have to occur, and only then would you get the exception. Possibly bombing your server in production. We already have options 1 and 2 available to us. You can use something that a type checker can verify, you can use something that every human can verify, and you can even do both if you're paranoid. Do you really need more than that? Remember: The thing that you declare Final will, some day, need to be changed. Probably as part of your own test suite (you do have one of those, right?). With MyPy, you could tell it not to validate that line, or that file. With a naming convention, you can explain the need for the change with a comment. How are you going to override the language feature? ChrisA

Reply to Chris: Wait. Deployment? Before deploying we *run the code* at least once and then we get the errors. And I'm not sure but type checking is ok but sometimes enforcing is a better option. Why? Mypy or typecheckers have to be run manually and they are also not part of the stdlib. Enforcing can help us catch it during editing because we don't deploy before running it at least once. So we're writing our own code and we don't run it before deploying? Mypy will catch at step 2 but enforcing can help as catch at step 1.

On Tue, May 25, 2021 at 5:24 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Check out pycharm with the mypy plugin. It will flag up typing.Final violations before you even finish typing the line containing the error. You can see the plugin in action in this short gif: https://imgshare.io/image/mypy.p8Gy4d

I actually suggest a different kind of Constant in Python not the classical const we see in Java or C/C++. Constants doesn't mean we can't reassign the name to a different value. Constants behave like literals. They are sort of literals actually. We reference a value by a name. Variable is just a reference to a value and can change to provide reference to another value. Constants on the other hand are name for a value. So constant is a name we can alternatively use for a value. So 10 is same as constant while 10 is same as the value dereferenced by variable. I propose this logic to work behind the scenes though not sure whether this is possible at all.

On Tue, May 25, 2021 at 06:09:29PM -0000, Shreyan Avigyan wrote:
So... constants aren't constant? They're variables?
Constants behave like literals. They are sort of literals actually.
In what way are they like literals? How do literals behave?
We reference a value by a name. Variable is just a reference to a value and can change to provide reference to another value.
Right. A variable is a name that references a value.
Constants on the other hand are name for a value.
Right. Constants are a name that references a value.
So constant is a name we can alternatively use for a value.
Indeed. The only difference between a constant and a variable is that constants cannot be rebound to a new value, but variables can. But you've said that you want constants to be capable of being rebound to a new value. So your constants are identical to variables.
So 10 is same as constant while 10 is same as the value dereferenced by variable.
I don't understand what you are trying to say here. -- Steve

Reply to Steven D'Aprano:
But you've said that you want constants to be capable of being rebound to a new value. So your constants are identical to variables.
No, not at all. Actually to be clear, constants are supposed to behave like literals but in implementation they are nothing more than names that are bind to a value.

Greetings, On Tue, May 25, 2021 at 8:13 PM Chris Angelico <rosuav@gmail.com> wrote:
It might seem that i am trolling, but i am not
How are you going to override the language feature?
By just removing the constant keyword? Kind Regards, Abdur-Rahmaan Janhangeer about <https://compileralchemy.github.io/> | blog <https://www.pythonkitchen.com> github <https://github.com/Abdur-RahmaanJ> Mauritius

On Wed, May 26, 2021 at 02:09:58AM +1000, Chris Angelico wrote:
That depends on how it is implemented. Here are some thoughts. Given: constant spam = "spam and eggs" we might be able to change it like this: # Rebinding a constant raises a warning. Just ignore the warning. try: spam = "spam spam spam spam" except Warning: pass # Write directly to the namespace. globals()['spam'] = "spam spam spam spam" # Command line switch or environment variable. python3.11 --no-constants testsuite.py # Context manager. with ConstantEnforcement(False): spam = "spam spam spam spam" # Special backdoor. sys.setconstant('spam', "spam spam spam spam") Enforcing constantness doesn't necessarily imply there is no way to disable or work around it. Especially in a consenting adults language like Python, I would expect that there will be. (Most likely just write directly to the namespace.) -- Steve

On Wed, May 26, 2021 at 11:57 AM Steven D'Aprano <steve@pearwood.info> wrote:
And the very presence of such an option means that these things are not constant, meaning that the interpreter and the programmer cannot assume they are constant. Especially if there's a way to globally disable constness. (My guess is that writing directly to the namespace, or "import some_module; some_module.spam = 42", would bypass it.) So how long will it be before some library has a thing declared as a constant, and some user of that library overrides it in production? I give it a week, but only because I doubt that const would be heavily used. If it were, I'd give it a day. ChrisA

Reply to Chris: There are two things I want to say about constants :- 1) Global-Local Constants - The ALL_CAPS convention variables should become constant. 2) Class member constants - Constants should be used only for avoiding from being overridden. It should not be used as "We have a class. Use constants.". Sometimes if someone changes a critical value especially in Python a dynamically typed language it can have bad effects. Suppose we have a Windows and we go to registry editor and delete keys and set different values and then Windows won't boot up the next time. That's why class member constants is necessary.

On Wed, May 26, 2021 at 5:26 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
I don't understand this mentality. Firstly, Windows *does* have a registry editor. You can indeed break things. Windows won't stop you. Secondly, CPython comes with the ctypes module that allows you to *change the values of literals*. I'm not kidding. You can actually make it so that the integer 42 actually behaves as if it's 28. Do we really need to stop people from doing things because they're dangerous? There are many arguments in favour of constants, but this one strikes me as particularly weak. ChrisA

Chris wrote:
There are many arguments in favour of constants, but this one strikes me as particularly weak.
That's not exactly how I meant it. I was telling that while constant should be there it should not be used as "Use constants everywhere". I actually believe the main reason for constants should "Debugging". Our program can become unstable if something really important changes. And yeah sometimes stopping people from doing dangerous things is a good idea and sometimes not.

On Wed, May 26, 2021 at 07:25:09AM -0000, Shreyan Avigyan wrote:
2) Class member constants - Constants should be used only for avoiding from being overridden.
But you said that constants can be overriden and the name rebound. Is your proposal only to add a keyword "constant" that does absolutely nothing? If that is not your proposal, please explain what you want the keyword to do. So far you have just confused me, and I don't think I'm the only one. You have said that constants can be rebound to new values, but then you say they are used to stop them from being overridden. You said that constants act just like literals, but you won't tell us how literals act. Let me see if I can get some clarity: If I define a constant: constant pi = 3.14 what happens when I try to rebind it later? # later pi = 3.1415
The Windows registry isn't a class. The Windows registry doesn't prevent you from changing values or deleting keys. If the keys and values in the registry were constant, then you couldn't control the behaviour of Windows, or install new applications, drivers, printers, hardware, etc. So I don't think the registry is a good example. -- Steve

Reply to Steven - Literals mean 10 or 20. Can you assign something to literal? No. But you can assign something to a variable to point to another value. That's why I said constants should behave like literals. Point is constants are names bind to a value. We can change the value but not the name to point to a different value. Simply think "const type *pointer" not "type *pointer const". And by debugging I meant it would immediately be detectable if someone tries to change the constant or not. (Not a good argument in constant's favor though)

On 5/26/21 5:55 AM, Shreyan Avigyan wrote:
I think the biggest problem is that basically, by definition, to actually enforce this requires a attribute lookup on EVERY rebinding or mutating operation. One stated goal was that constant mylist = [1, 2, 3] was to generate an error if you do mylist.append(4) but that also means we need to catch yourlist = mylist yourlist.append(4) and since that binding could happen on a cross module function call, every mutating operation needs a run-time check to see if this object has been bound to a constant. We also need to test everytime we bind/rebind a name, as the creation of that constant binding might be hidden behind mechanisms that we can't determine at compile time. Things like can another module inject a constant into our module? If so, we need to check every name binding to see if that name has been made a constant. This can add up to a noticable run time cost. Using the alternative of type hints says that the checker can warn us of many of the violations, and you have the OPTION of turning on run time checking if you think you have a problem (or you can just choose to always turn it on and pay the cost). -- Richard Damon

Reply to Richard Damon: The values can be changed. It can be mutated (if mutable). This idea suggests we can't reassign anything to the name. Suppose, constant x = ["List"] x.append("something") # OK x = [] # Error Think of it as a const *ptr. Don't think of it as const *ptr const.

On Wed, May 26, 2021 at 11:26:17AM -0000, Shreyan Avigyan wrote:
At last, a straight answer to the question of what this does. Thank you. Your answer here crossed with my previous post. In the future, can you please try to use standard Python terminology instead of ambiguous language? "The values can be changed" is ambiguous. That can mean mutating an object: x.append(None) # mutation changes the value of x and it can mean rebinding: x = None # rebinding changes the value of x So now tell us, what sort of error do you get? Is it a compile-time error or a run-time error?
Think of it as a const *ptr. Don't think of it as const *ptr const.
What's a const *ptr and a const *ptr const? -- Steve

On Wed, 26 May 2021 at 12:55, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
What's a const *ptr and a const *ptr const?
In C, a const pointer means a pointer that can only point to one value while const pointer const means a pointer that can only point to one constant value.
Python has names that bind to values. They are *not* equivalent to pointers that point to variables. So I suspect that the communication problem here is that you aren't thinking of what you're trying to propose in Python terms, hence everyone else (who is!) is misunderstanding you. You need to make sure that you properly understand Python's name binding mechanisms before proposing to change them... (Maybe you do, in which case can you please express your ideas in terms of those mechanisms, not in terms of C). Paul

Reply to Paul Moore: In Python terms, a constant is a name that binds itself to a value in memory and that name cannot bind itself to a different value now (unlike variables). The value can be mutated (if mutable) but the name cannot bind to a different value once it has bind itself to a value.

On Wed, May 26, 2021 at 1:10 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Given Python already has a method for annotating a variable (name binding) as 'constant', by using typing.Final, I don't see any need for an alternative syntax for this. Wouldn't it be simpler to just propose making the compiler/runtime enforce typing.Final annotations at compile and/or run-time? PEP 591 has all of the definitions and explanation pre-written for you, so that would avoid all the terminology wrangling too ;)

Reply to Stestagg: That's annotation to make sure no one uses a name. This is a proposal to implement constant name binding in Python. There are many applications of constants.

On Wed, May 26, 2021 at 10:37 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Reply to Stestagg:
That's annotation to make sure no one uses a name. This is a proposal to implement constant name binding in Python. There are many applications of constants.
Yes, and you've given us zero of them. Can you show us how constants would be beneficial as part of the language, where ALL_CAPS_NAMES and typing.Final would not be beneficial? ChrisA

I've already given one. Since Python is dynamically typed changing a critical variable can cause huge instability. Want a demonstration? Here we go, import sys sys.stdout = None Now what? Now how can we print anything? Isn't this a bug? There are lots of code out there where we need to protect things from being overwritten. Though I'm never telling to use constants in Python stdlib or else I could have never done this demonstration. :)

On Wed, May 26, 2021 at 10:53 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
A bug? Python did exactly what it should (with the possible exception, pun intended, that there could be an issue with None not being writable). That module attribute exists specifically to be overwritten. Are you saying that this should be a constant? This isn't "instability". This is the interpreter doing exactly what you told it to. Plus - this wouldn't even apply to the proposal as given, which doesn't affect module assignment, only assignment to globals. If this is the best justification you can give, I don't think this proposal is very strong. ChrisA

Reply to Chris: Yes I know that. sys.stdout exists there for that reason only. But if we can't print then it means we changed it somewhere. I just gave an example. I've seen code where constants can be really necessary. Python lets us use these things because it's a programming language. But libraries are not programming languages. They are there to enhance the programming. Suppose we're testing out a library. Now maybe there's something really critical we shouldn't change. We must access but mustn't change. And we mistakenly changed it. Now we're not gonna deploy our application. So we don't run a type checker. Now we run and the program crashes and yet we can't find where is the bug. Why should tools be needed to solve such an obvious problem? Why can't Python itself help us like it does when we add int and str?

On Wed, May 26, 2021 at 11:20 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Reply to Chris:
Yes I know that. sys.stdout exists there for that reason only. But if we can't print then it means we changed it somewhere. I just gave an example.
Yes, an example where you're trying to show... something. Which I still don't understand, because you're advocating constants, and then citing an example of something that is specifically there for the purpose of being changed/replaced.
I've seen code where constants can be really necessary. Python lets us use these things because it's a programming language. But libraries are not programming languages. They are there to enhance the programming. Suppose we're testing out a library. Now maybe there's something really critical we shouldn't change. We must access but mustn't change. And we mistakenly changed it. Now we're not gonna deploy our application. So we don't run a type checker. Now we run and the program crashes and yet we can't find where is the bug. Why should tools be needed to solve such an obvious problem? Why can't Python itself help us like it does when we add int and str?
So........ if you think you have a problem, your program crashes, and you're having trouble finding the bug... then...... maybe that would be a good time to use a type checker? I don't know. Your examples are so extremely vague, and you're assuming that (a) the library author can and does declare that something shouldn't ever be changed, (b) the program author can and does attempt to change it, (c) the library author was correct and the program author was wrong, and (d) the program author couldn't figure this out by the fact that the name was in ALL_CAPS. That is a lot of big assumptions, especially the third one. Plus, you're assuming that this constness will be detected if the mutation happens in some other module, which is NOT part of several iterations of the proposal. Need a more specific example. Yaknow, an actual example, not just "this might happen in some universe". Figure out what you're actually proposing. Figure out what real problems it is supposed to be able to solve. The problems you're solving should govern the feature you're proposing. ChrisA

Well. How can I go beyond why constant was invented in the first place? As far as I can understand by my limited knowledge that *sometimes* constants can be useful, sometimes they are terrible. The main reason constant was invented was to provide an additional support to programmers so that they don't make a program unstable. And there are code that really have this problem. Python can be "Consenting Adults" language but so are others they just don't claim it. Programming is programming, it depends on the user and author what they'll choose to do "enforcing" or "consenting adults". And I must confess I don't like type checking in this context because Python should itself help us point out these relatively small but extremely important bugs related to constants (or Final). And constants doesn't make code fast. To avoid decreasing current performance, I propose to add new OP code and new AST node for constants so that they don't mingle with variables. And all constant assignment checking should be done at runtime not compile time. I hope this actually answers all of the questions coming up till now.

On 2021-05-26 at 12:53:32 -0000, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
In C: fclose(stdout); Now what? Now how can we print anything? (I'm sure other languages allow programs to close stdout, but C has come up in this thread before.) There are a handful of real use cases for closing (or otherwise changing) stdout; e.g., background/daemon processes, programs that operate exclusively in a graphical environment.

On Wed, May 26, 2021 at 12:53:32PM -0000, Shreyan Avigyan wrote:
Assigning to None is probably a TypeError, because None doesn't implement the file object interface. But assigning to a file is perfectly correct. stdout, stdin and stderr are designed to be assigned to. That's why the sys module defines `sys.__stdout__` etc, so you can easily restore them to the originals. You should also note that there are many circumstances where sys.stdout etc are all set to None. See the documentation. https://docs.python.org/3/library/sys.html#sys.__stdin__ -- Steve

I have certainly overridden sys.stdout, it's USEFUL, Shreyan. (One use case was to make programs pause after each screenful of output. Probably I've also used it to suppress output.) But I didn't know about sys.__stdout__, thanks Steven. And yes, I'm at least as confused as to what the proposal is as anybody else. Rob Cliffe On 26/05/2021 14:20, Steven D'Aprano wrote:

On 2021-05-26 13:53, Shreyan Avigyan wrote:
Actually, I've done something like that. import codecs import sys sys.stdout = codecs.getwriter('utf-8')(sys.stdout.detach()) This was because I was running a script from an editor and it picked up the wrong output encoding, raising an error when I tried to print characters that were outside the ASCII range. Thankfully that problem is now solved, but it was a useful workaround.

Shreyan Avigyan writes:
With print(). Even in your code, it still works, for an appropriate definition of "works". See next comment.
Isn't this a bug?
Maybe, but if it is, it's a bug because you didn't sys.stdout.close() before rebinding it.[1] sys.stdout = None is useful; it has the effect of sys.stdout = open("/dev/null", "w") ie, shutting up a chatty program. (This surprised me; I thought print would raise because None has no 'write' attribute. It's an 'is None' test, not a bool test; it does complain on sys.stdout = 0. It really doesn't like 0, it complains again -- about 'flush' -- on interpreter exit. :-)
There are lots of code out there where we need to protect things from being overwritten.
Perhaps you could give a more persuasive example. The whole idea is against the usual "consenting adults" approach of Python. Or as that famous Python programmer Barack Obama put it, "let's try not to do stupid shit." :-) Footnotes: [1] I suspect there's a race condition in some environments. At least up to version 9, MacOS poll(2) and X.org were buggy together. The event loop would poll(), and if you had an open file descriptor that hadn't been closed before fork but was somehow unused in the child, it would infloop on an OS error from poll(). So if you fork before sys.stdout gets GC'd and therefore .close()'d, you could end up with such a zombie fd.

On Wed, May 26, 2021 at 1:38 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Reply to Stestagg:
That's annotation to make sure no one uses a name.
No.
This is a proposal to implement constant name binding in Python.
Here are some excerpts from pep 591, specifically, how are they different from what you are proposing? [typing.Final purpose] "Declaring that a variable or attribute should not be reassigned" and " The two main rules for defining a final name are: - There can be *at most one* final declaration per module or class for a given attribute. There can't be separate class-level and instance-level constants with the same name. - There must be *exactly one* assignment to a final name. " and [typing.Final is useful in...]"Allowing a name to be used in situations where ordinarily a literal is expected (for example as a field name for NamedTuple, a tuple of types passed to isinstance, or an argument to a function with arguments of Literal type [3] <https://www.python.org/dev/peps/pep-0591/#pep-586>)."
There are many applications of constants.
However many there may be, the cases discussed here all fall under this definition

On 5/26/21 7:40 AM, Steven D'Aprano wrote:
So we only have to check at runtime if name has been marked constant to handle cases like: if flag: constant foo = ["List"] else: foo = ["Other"] or import mymodule constant mymodule.foo = ["List"] or are you going to restrict how constants are created?
He is assuming that people are familiar with C. -- Richard Damon

On Wed, May 26, 2021 at 9:20 PM Richard Damon <Richard@damon-family.org> wrote:
That's basically going to be impossible, especially since the object could reference OTHER objects which could change, too. Plus, it should be perfectly valid to say mylist.index(4) even if you can't do mylist.append(4), so you can't just guard against method calls. Enforcing immutability externally is basically impossible. Don't even bother trying. A more viable proposal might be "this name represents this literal", and should always be interpreted identically to that literal. So if you have that constant declaration, then every use of mylist is exactly equivalent to writing [1, 2, 3] at that exact point in the code (constructing a new list every time). That'd be well-defined and plausibly behaved, while still strongly recommending that immutables be used (since the compiler can constant-fold ints/floats/tuples/strings). The value would be that if you *edit* the constant (not rebind it, actually edit the source code), it would change in all places. I don't think it's of great value and I'm not recommending it, but it would at least be a sane and plausible proposal, without logical contradictions. ChrisA

Shreyan, you have probably spent hundreds, maybe thousands of words to avoid giving straightforward, direct, explicit answers to the questions, preferring to force us to guess what you mean from vague analogies. Please stop. Please give an explicit description of the functional requirements of this proposed feature. Give code samples showing the exact behaviour. Stop expecting us to guess what you mean when your statements contradict themselves. what happens when I try to rebind a constant? constant pi = 3.14 # later pi = 3.1415 For the record, this is not a rhetorical question. I do not know the answer, I have asked at least twice before what happens when we rebind a constant, *and you won't tell me*. On Wed, May 26, 2021 at 09:55:41AM -0000, Shreyan Avigyan wrote:
Literals mean 10 or 20. Can you assign something to literal? No.
Constants aren't literals, they are names. `10` is a literal, but `pi` is a constant. Can you assign to a name? Yes, otherwise we can't give the constant a value in the first place. Can you rebind that name? You have said yes. You wrote: "Constants doesn't mean we can't reassign the name to a different value." https://mail.python.org/archives/list/python-ideas@python.org/message/NTDZ4E... Actually, I think most people think that being a constant *does* mean that you can't reassign the name to a different value. Otherwise its not a constant, its just a variable. But in the same post I linked to above you explicitly ruled out classic constants like found in C and Java (and Pascal, and other languages). So now I have *no idea* what your constants are, since apparently they behave exactly like variables: they are names that refer to values, and you can rebind them to new values.
What do you mean by "change the value"? Are you talking about mutable objects? Earlier you said that we can change the name to point to a different value, now you say that we can't.
Simply think "const type *pointer" not "type *pointer const".
That means nothing to me. This is a proposal for a feature in Python, please describe it in terms of existing Python behaviour, not whatever language that is.
How would it be detectable? Please stop expecting us to guess what will happen. **Tell us what will happen.** It shouldn't be this hard to get information out of somebody proposing a new language feature :-( -- Steve

Reply to Steven - Sorry for creating confusions. 1. Leave debugging. As I said that's not a good argument for the existence of constants. 2. "Constants doesn't mean we can't reassign the name to a different value." was differently intended. I was trying to say that we should *treat* it like a *literal*. I never said constant will be a type of variable. I was a little bit unclear in that message. 3. Constant will be constant name binding. Value can be mutated (if mutable) but the name cannot be reassigned. 4. constant pi = 3.14 # later pi = 3.1415 # Error

On Wed, 26 May 2021 at 12:59, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Steven's already asked what error, and is it compile time or runtime. I'll add foo.py: constant x = 12 bar.py import foo foo.x = 19 You can only detect this at runtime. baz.py import foo name = 'x' setattr(foo, name, 22) You can only detect this at runtime. Also, if some_condition: constant a = 1 else: a = 2 a = 3 Is this allowed? Is a constant? What about for i in range(10): constant a = [] Is this allowed, or is it 10 rebindings of a? for i in range(10): constant a = [i] What about this? What is a "literal" anyway? You've used lists in examples (when discussing mutability) but list displays aren't actually literals. Lots of questions. I'm sure it's possible to answer them. But as the proposer, *you* need to give the answers (or at least give a complete and precise enough specification that people can deduce the answers). At the moment, everyone is just guessing. Paul

Reply to Paul Moore: if some_condition: constant a = 1 else: a = 2 a = 3 Yes this is allowed. This is runtime. for i in range(10): constant a = [] Not sure. Though it's preferable to be runtime. Preferable is "not allowed". And lists are also literals. Any Python Object that is not assigned to a variable is a literal. Python claims that itself. A preview - [10] = [2] SyntaxError: Can't assign to literal here. Constants should have a similar error - constant x = 10 x = [2] SomeErrorType: Can't assign to constant here.

On Wed, May 26, 2021 at 10:31 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
The literal that you can't assign to here is "10". You're perfectly allowed to assign to a list display: [x, y, z] = range(3)
Is it a syntax error? Be VERY specific here. It makes a huge difference. Also, what about this: constant x = 10 def f(): x = 20 SyntaxError? Runtime error? Shadowing? This is important and cannot be brushed aside. ChrisA

On Wed, May 26, 2021 at 10:37:34PM +1000, Chris Angelico wrote:
The x inside the function is just a local variable. The x outside the function and the x inside it are in different namespaces and shouldn't clobber each other any more than variables in different namespaces do. Constants in statically typed languages are easy, because the compiler knows which names are constant at compile-time and can disallow assignments to those names without any runtime cost. Constants in dynamically typed languages like Python are hard, because we either have to severely restrict their use so that they can be determined at compile-time, which goes against the dynamic nature of the language, or else we have to slow down every assignment to check whether it is a constant or not. So the irony is that constants can speed up statically typed languages, but slow down dynamically typed ones. -- Steve

On Wed, May 26, 2021 at 11:15 PM Steven D'Aprano <steve@pearwood.info> wrote:
I'd kinda expect that too, but given the uncertainties in the rest of this thread, I wasn't certain. I'm fairly lost as to what the proposal even is, though. It seems to morph every time it gets poked. ChrisA

On Wed, 26 May 2021 at 13:13, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Reply to Paul Moore:
In Python terms, a constant is a name that binds itself to a value in memory and that name cannot bind itself to a different value now (unlike variables). The value can be mutated (if mutable) but the name cannot bind to a different value once it has bind itself to a value.
"value in memory" isn't Python terminology. Nor is "variable". Please point me to the exact sections in the language reference if you feel I'm wrong here. In particular, Python refers to "identifiers" and "names" - see https://docs.python.org/3/reference/lexical_analysis.html#identifiers ("Identifiers (also referred to as names) are described by the following lexical definitions.") On Wed, 26 May 2021 at 13:31, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
OK, so a=3 may raise an exception at runtime (depending on the value of some_condition). We need to decide exactly what exception would be used but I'm OK with that. It's specifically *not* a syntax exception, though, as that's a compile-time exception.
I don't know what you mean here. Are you saying that you'd prefer it not to be allowed? It's your proposal, the decision is up to you.
And lists are also literals. Any Python Object that is not assigned to a variable is a literal. Python claims that itself. A preview -
Read the language reference here: https://docs.python.org/3/reference/lexical_analysis.html#literals
[10] = [2] SyntaxError: Can't assign to literal here.
As Chris pointed out, this is flagging the assignment to the literal 10. Have you properly researched the existing semantics to make sure you understand what you're proposing to change, or are you just guessing?
But you just said it was runtime, so it definitely *isn't* similar to the syntax error "Can't assign to literal here". You're making inconsistent statements again :-( On Wed, 26 May 2021 at 13:53, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
However, there's a *huge* amount of extremely useful code out there that does assign to sys.stdout - pytest's mechanisms for capturing test output use this, for example. Making sys.stdout constant would break this. I don't think you've thought this proposal through at all, to be honest. You seem to be making up answers as the questions arise, which is *not* what people are asking for here. We are asking that you *explain* your proposal, assuming that you already know the answers and are simply struggling to communicate the details. Paul

Reply to Paul Moore:
That's exactly why I wrote SomeErrorType instead of SyntaxError. They are never similar. I just said the behavior would seem similar.
I'm trying my best. But sometimes there are few questions coming up that confuses and I'm not sure of the answer and I'm ending up making inconsistent statements.

On Wed, 26 May 2021 at 14:33, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Your precise comment was
See above? You said "Constants should have a similar error" and then in your response to my reply, you said "That's exactly why I wrote SomeErrorType instead of SyntaxError. They are never similar. I just said the behavior would seem similar." How am I to interpret that? Your statements are directly contradictory ("similar error" vs "never similar"). And for information, I personally don't find compile time and runtime exceptions to "seem similar" at all, especially in the context of this discussion where we've repeatedly tried to get you to decide whether you're talking about compile time or runtime.
I'm trying my best. But sometimes there are few questions coming up that confuses and I'm not sure of the answer and I'm ending up making inconsistent statements.
So stop replying instantly, read the feedback, think your proposal through, and when you have an updated proposal that actually addresses all of the flaws people have pointed out with your current proposal, post that (with a clear explanation of what you changed and why). Knee-jerk replies from you aren't respectful of the time people are putting into trying to follow what you're proposing :-( Paul PS I know there's a tendency for people to reply very quickly on this list, and I know that can leave people feeling under pressure to reply quickly themselves. But it's a false pressure - some people simply need time to digest feedback, others are having to handle a language they are not a native speaker of, still others may have other reasons for needing extra time. If you're in a situation like that, please *do* take that time. We won't complain if you take time to compose your answers.

On Wed, May 26, 2021 at 12:31:07PM -0000, Shreyan Avigyan wrote:
Do you have any suggestions for how this should be implemented, how the interpreter will know at runtime whether or not the name `a` is a constant?
That is equivalent to: constant a = [] constant a = [] constant a = [] # seven more times If you can't rebind constants, then that better be disallowed. Otherwise you have rebound the constant `a` nine times: initialised it to one list on the first loop, and then the next nine loops you bind the same name to nine different lists.
And lists are also literals.
Lists are not literals. The documentation calls them *displays*. You won't find lists under "literals": https://docs.python.org/3/reference/expressions.html#literals but you will find them here: https://docs.python.org/3/reference/expressions.html#list-displays I admit that I too often wrongly refer to expressions like `[1, 2]` as a list literal, but that language is not technically correct for Python.
Any Python Object that is not assigned to a variable is a literal.
That's incorrect. When I write the expression: print(a*x + b) the object returned by `a*x + b` is not assigned to anything, but it's not a literal.
You are misinterpreting the error. If you look at the pointer in the syntax error, it points at the 10, not the list: >>> [10] = [2] File "<stdin>", line 1 [10] = [2] ^ SyntaxError: cannot assign to literal That statement is doing sequence unpacking: it unpacks the list [2], and the target list [10], and tries to assign 10 = 2 which is forbidden because 10 is a literal, *not* because `[10]` is a literal. (It isn't.) You can see a better example here: >>> [a, b, c, 10, d, e] = range(6) File "<stdin>", line 1 [a, b, c, 10, d, e] = range(6) ^ SyntaxError: cannot assign to literal -- Steve

if you want it for debugging, then static type checkers can do that for you. 2) This would allow another branch of OOP programming to enter Python.
Read-only member variables. There are Private members in Java and C++.
Then use Java or C++ -- the benefits of Python are not primarily the syntax, but it's dynamic nature. Though this does bring up an interesting (to the ignorant of most things typing me) question: Do the static type checkers support the concept of private attributes? That might be a nice feature. -CHB ----- Chris Barker Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

Hello, On Tue, 25 May 2021 11:53:20 -0000 "Shreyan Avigyan" <pythonshreyan09@gmail.com> wrote:
The idea of introducing constants on the core language level in Python is well-known topic and was brought up on multiple occasions both on the python-ideas and python-dev mailing lists. A random example: https://www.mail-archive.com/python-dev@python.org/msg110424.html "constants in Python: Starting simple and gradually adding more features, was: Re: Pattern Matching controversy" The matter is actually implementing it in different Python implementations. And some implementations had support for *some kind* of constants for many years (e.g. MicroPython with it's pre-annotation syntax of "FOO = const(1)"), while CPython still has it only on the level of external first-generation annotation module, "typing". As another example, I can give "strict mode" (https://www.mail-archive.com/python-ideas@python.org/msg25403.html) feature of my Python dialect, Pycopy (https://github.com/pfalcon/pycopy), which implements some (but again, not all) aspects of const'ness. E.g.: === print("In import-time") a: const = 1 a: const = 2 def __main__(): print("In run-time") global a a = 3 === Gives: ==== In import-time Warning: strict mode: overriding (monkey-patching) const name 'a' In run-time Traceback (most recent call last): File "strict.py", line 9, in __main__ RuntimeError: strict mode: cannot override const name ==== -- Best regards, Paul mailto:pmiscml@gmail.com

Now it seems Python doesn’t need constant. There are many ways we can achieve constants. Though constants may increase performance. Yet again it can also be the opposite. ________________________________ From: Paul Sokolovsky <pmiscml@gmail.com> Sent: Saturday, May 29, 2021 12:44:09 AM To: Shreyan Avigyan <pythonshreyan09@gmail.com> Cc: python-ideas@python.org <python-ideas@python.org> Subject: [Python-ideas] Re: Introduce constants in Python (constant name binding) Hello, On Tue, 25 May 2021 11:53:20 -0000 "Shreyan Avigyan" <pythonshreyan09@gmail.com> wrote:
The idea of introducing constants on the core language level in Python is well-known topic and was brought up on multiple occasions both on the python-ideas and python-dev mailing lists. A random example: https://www.mail-archive.com/python-dev@python.org/msg110424.html "constants in Python: Starting simple and gradually adding more features, was: Re: Pattern Matching controversy" The matter is actually implementing it in different Python implementations. And some implementations had support for *some kind* of constants for many years (e.g. MicroPython with it's pre-annotation syntax of "FOO = const(1)"), while CPython still has it only on the level of external first-generation annotation module, "typing". As another example, I can give "strict mode" (https://www.mail-archive.com/python-ideas@python.org/msg25403.html) feature of my Python dialect, Pycopy (https://github.com/pfalcon/pycopy), which implements some (but again, not all) aspects of const'ness. E.g.: === print("In import-time") a: const = 1 a: const = 2 def __main__(): print("In run-time") global a a = 3 === Gives: ==== In import-time Warning: strict mode: overriding (monkey-patching) const name 'a' In run-time Traceback (most recent call last): File "strict.py", line 9, in __main__ RuntimeError: strict mode: cannot override const name ==== -- Best regards, Paul mailto:pmiscml@gmail.com _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@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/PHU3YE... Code of Conduct: http://python.org/psf/codeofconduct/

On Tue, May 25, 2021 at 9:55 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
https://docs.python.org/3/library/typing.html#typing.Final Already exists :) ChrisA

Yes I'm aware of that. That's for typing. I'm talking about implementing it in Python itself not Python typing.

On Tue, May 25, 2021 at 4:02 PM Ethan Furman <ethan@stoneleaf.us> wrote:
I mean, it's part of the standard library, so we could argue on personal definitions of 'core Python', but it would be more useful to work out exactly what aspects of typing.Final are lacking for you. Why would this constant annotation need to be more strongly enforced than type checks? What actual benefit would this provide? Steve --

We've already had all of this since long before typing.py existed. ALLCAPS means "this value should only be set once and not changed." _underscore means "this is not part of public API, and it may change in next version. __double_under means "this is SERIOUSLY not something you should muck with, it's likely to break stuff if you do." Python should never become a B&D language like Java. If a user violates the contracts provided by code they utilize, that's on them, not on the language. On Tue, May 25, 2021, 11:28 AM Stestagg <stestagg@gmail.com> wrote:

On Tue, May 25, 2021 at 11:37:06AM -0400, David Mertz wrote:
We've already had all of this since long before typing.py existed.
ALLCAPS means "this value should only be set once and not changed."
`math.pi` says hello. The existence of math.pi and the fact that probably nobody ever has introduced a bug into their code by carelessly assigning another value to it suggests strongly that the benefit of language enforced constants is not high. But then on the other hand, who would ever want to change the value of pi?
_underscore means "this is not part of public API, and it may change in next version.
Namedtuple and the sys module also say hello. Namedtuple reserves single underscore names to itself, since any regular name could be a field name. So namedtuples have five public APIs starting with underscores. https://docs.python.org/3/library/collections.html#collections.namedtuple The sys module has numerous documented and apparently public functions starting with underscores, apparently to indicate that they are, or may be, implementation-dependent, or "internal and specialized purposes only", or for no given reason at all. https://docs.python.org/3/library/sys.html The naming conventions in the sys module are very inconsistent. Other functions that are clearly implementation-dependent have regular names, and some underscore names are in common use. E.g. _getframe.
__double_under means "this is SERIOUSLY not something you should muck with, it's likely to break stuff if you do."
That's a convention I've never heard of before. What's the difference between "private" and "SERIOUSLY private, I REALLY mean it"? The convention I know for double-underscores is that *in classes only* a name with a leading double-underscore is name-mangled in order to avoid naming conflicts with subclasses. Its not really more private than single-underscore names because it is transformed into a single- underscore name: __spam --> _classname__spam
Python is not really a design by contract language. Types are strongly checked, at runtime, and you can't turn that off. We have no syntactic support for contracts. A few third-party libraries have attempted to provide pre- and post-condition checking, but without syntactic support they are awkward to use. Its hard to blame people for violating a contract that they don't know applies, isn't an actual contract, and isn't enforced. The problem with naming conventions is that not everyone knows the conventions. Not every Python programmer spends hours every day on Python forums, reading Stackoverflow, etc. What you call "a B&D language like Java", other people might call "safety rails and warning lights". -- Steve

On Tue, May 25, 2021 at 9:36 PM Steven D'Aprano <steve@pearwood.info> wrote:
ALLCAPS means "this value should only be set once and not changed."
`math.pi` says hello.
(U ⇒ O) ⊬ (¬U ⇒ ¬O) The existence of math.pi and the fact that probably nobody ever has
Well, rarely. I've once or twice carelessly tested or taught some iterative algorithm for generating pi and made the error of overwriting the name. In this particular example, and also math.e and math.tau, the lowercase follows external conventions. From time to time other such mathy constants too. But the UPPERCASE constants are pretty darn common, pre-dating Python.
Yeah. There are a few exceptions. I'll note... perhaps *confess*... that even though I was literally sitting at the desk next to Raymond when he was thinking about the design of namedtuple, and even discussed it with him slightly, I had assumed for more years than I wish to mention that ._asdict and ._fields were not strictly promised because of their names.
In classes, it's name mangling. I've found that most people, or most code I've seen, treat module or function level `__double` names as implicitly similar. That's certainly what I do myself. If I don't want someone to rely on a name in future versions of some module, I'll use the single underscore. If I want to warn them that relying on it might cause bad things even right now, in this version, I'll use the double leading-underscore. Obviously it's not enforced at a language level. But it's a pretty good clue for other users.
It seems like a documentation question then. PEP 8 describes it as: - _single_leading_underscore: weak "internal use" indicator. E.g. from M import * does not import objects whose names start with an underscore. - __double_leading_underscore: when naming a class attribute, invokes name mangling - __double_leading_and_trailing_underscore__: "magic" objects or attributes that live in user-controlled namespaces. E.g. __init__, __import__ or __file__. Never invent such names; only use them as documented. It's not 100% explicit about double underscore meaning "I REALLY mean it" ... but it's kinda implied in the contrast with single underscore. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.

On Tue, May 25, 2021 at 04:27:41PM +0100, Stestagg wrote:
On Tue, May 25, 2021 at 4:02 PM Ethan Furman <ethan@stoneleaf.us> wrote:
The actual enforcement perhaps? Without mypy, which is not part of the standard library or core language, annotating something with typing has no more benefit than a naming convention -- and likely less, since naming conventions are visible every time you refer to the name, not just at the point of creation.
Why would this constant annotation need to be more strongly enforced than type checks? What actual benefit would this provide?
I guess that comes down to your position on how strongly you care about backwards compatibility for people who ignore your (not-so?) clear documentation. If you take the Microsoft position of trying very hard not to break the code of people even if they have used undocumented internals or clearly documented private areas, you might want the interpreter to do more to discourage those practices. If you take the Apple position of not giving a damn whose code you break, then you might not. -- Steve

It's still a Python feature even if it's not a language feature, it's well defined by PEP and any type checker wanting to implement type hinting to spec must include it. Further type hinting allows developers who want to use a Constant / Final feature get the benefit already from type hinting, whereas developers who want to stick to the pythonic philosophy of "we're all consenting adults here" can still mess with the code but know it's not supported at all. As far as I can see it's the best of both worlds. Could you please list in your proposal the real world benefits vs. the existing type hinting feature? On Tue, May 25, 2021 at 11:02 AM Ethan Furman <ethan@stoneleaf.us> wrote:

On 5/25/21 8:33 AM, Damian Shaw wrote:
To be clear, this is not my proposal, and I completely agree with David Mertz. I am -1 on the proposal. Nevertheless, the proposal is not satisfied by a stdlib module that is completely ignored by the Python runtime -- that was the only point I was trying to make. -- ~Ethan~

First it would seem useless or not necessary but soon this becomes clear. 1) It would help programmers debug their code easily and help them find out that the bug's not in the constant, that's for sure. 2) This would allow another branch of OOP programming to enter Python. Read-only member variables. There are Private members in Java and C++. But I believe they are not their to hide things. They are there to debug.

On Tue, May 25, 2021 at 4:41 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
A reasonable type checker will detect cases where typing.Final annotated bindings are reassigned. For example, with mypy: https://mypy.readthedocs.io/en/stable/final_attrs.html If mypy is not catching cases of rebinding, then that would likely be treated as a bug by the maintainers. If you want help with debugging code, then a type checker will help with that.
that's for sure. 2) This would allow another branch of OOP programming to enter Python. Read-only member variables.
see above, this is already provided by mypy There are Private members in Java and C++. But I believe they are not their
to hide things. They are there to debug.
There are private methods in python too, just not enforced by the runtime, instead enforced by the myriad developer/debug tooling out there in the form of linters/checkers.

I'm aware that Python is a "Consenting Adults" language but I think Python should provide this functionality at least. This idea doesn't actually make it like Java. Because why should we mess around with that member anyway. Sometimes we want to provide members that users must use but mustn't change. Well if the user does, then "crash"! Suppose we have filename member and we must access that but if we change it by any chance then what will happen is Python will not close the file instead it will create a new file leaving it in an undefined state - "Half written half not".

On Wed, May 26, 2021 at 2:00 AM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
I'm aware that Python is a "Consenting Adults" language but I think Python should provide this functionality at least. This idea doesn't actually make it like Java. Because why should we mess around with that member anyway. Sometimes we want to provide members that users must use but mustn't change. Well if the user does, then "crash"! Suppose we have filename member and we must access that but if we change it by any chance then what will happen is Python will not close the file instead it will create a new file leaving it in an undefined state - "Half written half not".
At best, Python could check this at run time, after it's too late. Type annotations and a tool like mypy can check it at, effectively, compilation time. All you have to do is run mypy on your code, and you'll know if that private member got assigned to. That's its point! What advantage would be gained by adding this functionality to the language interpreter, as opposed to being in the typing module and verified by an external tool? Do you really need your validation to be done later and in an unreliable way? There are four points at which a bug could be detected: 1) Editing 2) Deployment 3) Compilation (in Python's case, that happens on first import of the module) 4) Execution (an exception when you actually attempt the mutation) #1 is achieved by ALL_CAPS_NAMING_CONVENTION, because if anyone attempts to assign to it, it's obvious that something's wrong. That's the absolute earliest that you could possibly notice the bug, and *we already have that feature* built into our own brains. #2 would be where MyPy would catch the bug. Include a type checker pass in your pre-deployment checklist, possibly enforced by your pull request system, or verified automatically as part of CI. I'm not sure how you could catch this kind of thing at phase #3, but it might be possible. #4 is where you'd actually catch it, if your proposal were to be implemented. The mutation would have to occur, and only then would you get the exception. Possibly bombing your server in production. We already have options 1 and 2 available to us. You can use something that a type checker can verify, you can use something that every human can verify, and you can even do both if you're paranoid. Do you really need more than that? Remember: The thing that you declare Final will, some day, need to be changed. Probably as part of your own test suite (you do have one of those, right?). With MyPy, you could tell it not to validate that line, or that file. With a naming convention, you can explain the need for the change with a comment. How are you going to override the language feature? ChrisA

Reply to Chris: Wait. Deployment? Before deploying we *run the code* at least once and then we get the errors. And I'm not sure but type checking is ok but sometimes enforcing is a better option. Why? Mypy or typecheckers have to be run manually and they are also not part of the stdlib. Enforcing can help us catch it during editing because we don't deploy before running it at least once. So we're writing our own code and we don't run it before deploying? Mypy will catch at step 2 but enforcing can help as catch at step 1.

On Tue, May 25, 2021 at 5:24 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Check out pycharm with the mypy plugin. It will flag up typing.Final violations before you even finish typing the line containing the error. You can see the plugin in action in this short gif: https://imgshare.io/image/mypy.p8Gy4d

I actually suggest a different kind of Constant in Python not the classical const we see in Java or C/C++. Constants doesn't mean we can't reassign the name to a different value. Constants behave like literals. They are sort of literals actually. We reference a value by a name. Variable is just a reference to a value and can change to provide reference to another value. Constants on the other hand are name for a value. So constant is a name we can alternatively use for a value. So 10 is same as constant while 10 is same as the value dereferenced by variable. I propose this logic to work behind the scenes though not sure whether this is possible at all.

On Tue, May 25, 2021 at 06:09:29PM -0000, Shreyan Avigyan wrote:
So... constants aren't constant? They're variables?
Constants behave like literals. They are sort of literals actually.
In what way are they like literals? How do literals behave?
We reference a value by a name. Variable is just a reference to a value and can change to provide reference to another value.
Right. A variable is a name that references a value.
Constants on the other hand are name for a value.
Right. Constants are a name that references a value.
So constant is a name we can alternatively use for a value.
Indeed. The only difference between a constant and a variable is that constants cannot be rebound to a new value, but variables can. But you've said that you want constants to be capable of being rebound to a new value. So your constants are identical to variables.
So 10 is same as constant while 10 is same as the value dereferenced by variable.
I don't understand what you are trying to say here. -- Steve

Reply to Steven D'Aprano:
But you've said that you want constants to be capable of being rebound to a new value. So your constants are identical to variables.
No, not at all. Actually to be clear, constants are supposed to behave like literals but in implementation they are nothing more than names that are bind to a value.

Greetings, On Tue, May 25, 2021 at 8:13 PM Chris Angelico <rosuav@gmail.com> wrote:
It might seem that i am trolling, but i am not
How are you going to override the language feature?
By just removing the constant keyword? Kind Regards, Abdur-Rahmaan Janhangeer about <https://compileralchemy.github.io/> | blog <https://www.pythonkitchen.com> github <https://github.com/Abdur-RahmaanJ> Mauritius

On Wed, May 26, 2021 at 02:09:58AM +1000, Chris Angelico wrote:
That depends on how it is implemented. Here are some thoughts. Given: constant spam = "spam and eggs" we might be able to change it like this: # Rebinding a constant raises a warning. Just ignore the warning. try: spam = "spam spam spam spam" except Warning: pass # Write directly to the namespace. globals()['spam'] = "spam spam spam spam" # Command line switch or environment variable. python3.11 --no-constants testsuite.py # Context manager. with ConstantEnforcement(False): spam = "spam spam spam spam" # Special backdoor. sys.setconstant('spam', "spam spam spam spam") Enforcing constantness doesn't necessarily imply there is no way to disable or work around it. Especially in a consenting adults language like Python, I would expect that there will be. (Most likely just write directly to the namespace.) -- Steve

On Wed, May 26, 2021 at 11:57 AM Steven D'Aprano <steve@pearwood.info> wrote:
And the very presence of such an option means that these things are not constant, meaning that the interpreter and the programmer cannot assume they are constant. Especially if there's a way to globally disable constness. (My guess is that writing directly to the namespace, or "import some_module; some_module.spam = 42", would bypass it.) So how long will it be before some library has a thing declared as a constant, and some user of that library overrides it in production? I give it a week, but only because I doubt that const would be heavily used. If it were, I'd give it a day. ChrisA

Reply to Chris: There are two things I want to say about constants :- 1) Global-Local Constants - The ALL_CAPS convention variables should become constant. 2) Class member constants - Constants should be used only for avoiding from being overridden. It should not be used as "We have a class. Use constants.". Sometimes if someone changes a critical value especially in Python a dynamically typed language it can have bad effects. Suppose we have a Windows and we go to registry editor and delete keys and set different values and then Windows won't boot up the next time. That's why class member constants is necessary.

On Wed, May 26, 2021 at 5:26 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
I don't understand this mentality. Firstly, Windows *does* have a registry editor. You can indeed break things. Windows won't stop you. Secondly, CPython comes with the ctypes module that allows you to *change the values of literals*. I'm not kidding. You can actually make it so that the integer 42 actually behaves as if it's 28. Do we really need to stop people from doing things because they're dangerous? There are many arguments in favour of constants, but this one strikes me as particularly weak. ChrisA

Chris wrote:
There are many arguments in favour of constants, but this one strikes me as particularly weak.
That's not exactly how I meant it. I was telling that while constant should be there it should not be used as "Use constants everywhere". I actually believe the main reason for constants should "Debugging". Our program can become unstable if something really important changes. And yeah sometimes stopping people from doing dangerous things is a good idea and sometimes not.

On Wed, May 26, 2021 at 07:25:09AM -0000, Shreyan Avigyan wrote:
2) Class member constants - Constants should be used only for avoiding from being overridden.
But you said that constants can be overriden and the name rebound. Is your proposal only to add a keyword "constant" that does absolutely nothing? If that is not your proposal, please explain what you want the keyword to do. So far you have just confused me, and I don't think I'm the only one. You have said that constants can be rebound to new values, but then you say they are used to stop them from being overridden. You said that constants act just like literals, but you won't tell us how literals act. Let me see if I can get some clarity: If I define a constant: constant pi = 3.14 what happens when I try to rebind it later? # later pi = 3.1415
The Windows registry isn't a class. The Windows registry doesn't prevent you from changing values or deleting keys. If the keys and values in the registry were constant, then you couldn't control the behaviour of Windows, or install new applications, drivers, printers, hardware, etc. So I don't think the registry is a good example. -- Steve

Reply to Steven - Literals mean 10 or 20. Can you assign something to literal? No. But you can assign something to a variable to point to another value. That's why I said constants should behave like literals. Point is constants are names bind to a value. We can change the value but not the name to point to a different value. Simply think "const type *pointer" not "type *pointer const". And by debugging I meant it would immediately be detectable if someone tries to change the constant or not. (Not a good argument in constant's favor though)

On 5/26/21 5:55 AM, Shreyan Avigyan wrote:
I think the biggest problem is that basically, by definition, to actually enforce this requires a attribute lookup on EVERY rebinding or mutating operation. One stated goal was that constant mylist = [1, 2, 3] was to generate an error if you do mylist.append(4) but that also means we need to catch yourlist = mylist yourlist.append(4) and since that binding could happen on a cross module function call, every mutating operation needs a run-time check to see if this object has been bound to a constant. We also need to test everytime we bind/rebind a name, as the creation of that constant binding might be hidden behind mechanisms that we can't determine at compile time. Things like can another module inject a constant into our module? If so, we need to check every name binding to see if that name has been made a constant. This can add up to a noticable run time cost. Using the alternative of type hints says that the checker can warn us of many of the violations, and you have the OPTION of turning on run time checking if you think you have a problem (or you can just choose to always turn it on and pay the cost). -- Richard Damon

Reply to Richard Damon: The values can be changed. It can be mutated (if mutable). This idea suggests we can't reassign anything to the name. Suppose, constant x = ["List"] x.append("something") # OK x = [] # Error Think of it as a const *ptr. Don't think of it as const *ptr const.

On Wed, May 26, 2021 at 11:26:17AM -0000, Shreyan Avigyan wrote:
At last, a straight answer to the question of what this does. Thank you. Your answer here crossed with my previous post. In the future, can you please try to use standard Python terminology instead of ambiguous language? "The values can be changed" is ambiguous. That can mean mutating an object: x.append(None) # mutation changes the value of x and it can mean rebinding: x = None # rebinding changes the value of x So now tell us, what sort of error do you get? Is it a compile-time error or a run-time error?
Think of it as a const *ptr. Don't think of it as const *ptr const.
What's a const *ptr and a const *ptr const? -- Steve

On Wed, 26 May 2021 at 12:55, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
What's a const *ptr and a const *ptr const?
In C, a const pointer means a pointer that can only point to one value while const pointer const means a pointer that can only point to one constant value.
Python has names that bind to values. They are *not* equivalent to pointers that point to variables. So I suspect that the communication problem here is that you aren't thinking of what you're trying to propose in Python terms, hence everyone else (who is!) is misunderstanding you. You need to make sure that you properly understand Python's name binding mechanisms before proposing to change them... (Maybe you do, in which case can you please express your ideas in terms of those mechanisms, not in terms of C). Paul

Reply to Paul Moore: In Python terms, a constant is a name that binds itself to a value in memory and that name cannot bind itself to a different value now (unlike variables). The value can be mutated (if mutable) but the name cannot bind to a different value once it has bind itself to a value.

On Wed, May 26, 2021 at 1:10 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Given Python already has a method for annotating a variable (name binding) as 'constant', by using typing.Final, I don't see any need for an alternative syntax for this. Wouldn't it be simpler to just propose making the compiler/runtime enforce typing.Final annotations at compile and/or run-time? PEP 591 has all of the definitions and explanation pre-written for you, so that would avoid all the terminology wrangling too ;)

Reply to Stestagg: That's annotation to make sure no one uses a name. This is a proposal to implement constant name binding in Python. There are many applications of constants.

On Wed, May 26, 2021 at 10:37 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Reply to Stestagg:
That's annotation to make sure no one uses a name. This is a proposal to implement constant name binding in Python. There are many applications of constants.
Yes, and you've given us zero of them. Can you show us how constants would be beneficial as part of the language, where ALL_CAPS_NAMES and typing.Final would not be beneficial? ChrisA

I've already given one. Since Python is dynamically typed changing a critical variable can cause huge instability. Want a demonstration? Here we go, import sys sys.stdout = None Now what? Now how can we print anything? Isn't this a bug? There are lots of code out there where we need to protect things from being overwritten. Though I'm never telling to use constants in Python stdlib or else I could have never done this demonstration. :)

On Wed, May 26, 2021 at 10:53 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
A bug? Python did exactly what it should (with the possible exception, pun intended, that there could be an issue with None not being writable). That module attribute exists specifically to be overwritten. Are you saying that this should be a constant? This isn't "instability". This is the interpreter doing exactly what you told it to. Plus - this wouldn't even apply to the proposal as given, which doesn't affect module assignment, only assignment to globals. If this is the best justification you can give, I don't think this proposal is very strong. ChrisA

Reply to Chris: Yes I know that. sys.stdout exists there for that reason only. But if we can't print then it means we changed it somewhere. I just gave an example. I've seen code where constants can be really necessary. Python lets us use these things because it's a programming language. But libraries are not programming languages. They are there to enhance the programming. Suppose we're testing out a library. Now maybe there's something really critical we shouldn't change. We must access but mustn't change. And we mistakenly changed it. Now we're not gonna deploy our application. So we don't run a type checker. Now we run and the program crashes and yet we can't find where is the bug. Why should tools be needed to solve such an obvious problem? Why can't Python itself help us like it does when we add int and str?

On Wed, May 26, 2021 at 11:20 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Reply to Chris:
Yes I know that. sys.stdout exists there for that reason only. But if we can't print then it means we changed it somewhere. I just gave an example.
Yes, an example where you're trying to show... something. Which I still don't understand, because you're advocating constants, and then citing an example of something that is specifically there for the purpose of being changed/replaced.
I've seen code where constants can be really necessary. Python lets us use these things because it's a programming language. But libraries are not programming languages. They are there to enhance the programming. Suppose we're testing out a library. Now maybe there's something really critical we shouldn't change. We must access but mustn't change. And we mistakenly changed it. Now we're not gonna deploy our application. So we don't run a type checker. Now we run and the program crashes and yet we can't find where is the bug. Why should tools be needed to solve such an obvious problem? Why can't Python itself help us like it does when we add int and str?
So........ if you think you have a problem, your program crashes, and you're having trouble finding the bug... then...... maybe that would be a good time to use a type checker? I don't know. Your examples are so extremely vague, and you're assuming that (a) the library author can and does declare that something shouldn't ever be changed, (b) the program author can and does attempt to change it, (c) the library author was correct and the program author was wrong, and (d) the program author couldn't figure this out by the fact that the name was in ALL_CAPS. That is a lot of big assumptions, especially the third one. Plus, you're assuming that this constness will be detected if the mutation happens in some other module, which is NOT part of several iterations of the proposal. Need a more specific example. Yaknow, an actual example, not just "this might happen in some universe". Figure out what you're actually proposing. Figure out what real problems it is supposed to be able to solve. The problems you're solving should govern the feature you're proposing. ChrisA

Well. How can I go beyond why constant was invented in the first place? As far as I can understand by my limited knowledge that *sometimes* constants can be useful, sometimes they are terrible. The main reason constant was invented was to provide an additional support to programmers so that they don't make a program unstable. And there are code that really have this problem. Python can be "Consenting Adults" language but so are others they just don't claim it. Programming is programming, it depends on the user and author what they'll choose to do "enforcing" or "consenting adults". And I must confess I don't like type checking in this context because Python should itself help us point out these relatively small but extremely important bugs related to constants (or Final). And constants doesn't make code fast. To avoid decreasing current performance, I propose to add new OP code and new AST node for constants so that they don't mingle with variables. And all constant assignment checking should be done at runtime not compile time. I hope this actually answers all of the questions coming up till now.

On 2021-05-26 at 12:53:32 -0000, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
In C: fclose(stdout); Now what? Now how can we print anything? (I'm sure other languages allow programs to close stdout, but C has come up in this thread before.) There are a handful of real use cases for closing (or otherwise changing) stdout; e.g., background/daemon processes, programs that operate exclusively in a graphical environment.

On Wed, May 26, 2021 at 12:53:32PM -0000, Shreyan Avigyan wrote:
Assigning to None is probably a TypeError, because None doesn't implement the file object interface. But assigning to a file is perfectly correct. stdout, stdin and stderr are designed to be assigned to. That's why the sys module defines `sys.__stdout__` etc, so you can easily restore them to the originals. You should also note that there are many circumstances where sys.stdout etc are all set to None. See the documentation. https://docs.python.org/3/library/sys.html#sys.__stdin__ -- Steve

I have certainly overridden sys.stdout, it's USEFUL, Shreyan. (One use case was to make programs pause after each screenful of output. Probably I've also used it to suppress output.) But I didn't know about sys.__stdout__, thanks Steven. And yes, I'm at least as confused as to what the proposal is as anybody else. Rob Cliffe On 26/05/2021 14:20, Steven D'Aprano wrote:

On 2021-05-26 13:53, Shreyan Avigyan wrote:
Actually, I've done something like that. import codecs import sys sys.stdout = codecs.getwriter('utf-8')(sys.stdout.detach()) This was because I was running a script from an editor and it picked up the wrong output encoding, raising an error when I tried to print characters that were outside the ASCII range. Thankfully that problem is now solved, but it was a useful workaround.

Shreyan Avigyan writes:
With print(). Even in your code, it still works, for an appropriate definition of "works". See next comment.
Isn't this a bug?
Maybe, but if it is, it's a bug because you didn't sys.stdout.close() before rebinding it.[1] sys.stdout = None is useful; it has the effect of sys.stdout = open("/dev/null", "w") ie, shutting up a chatty program. (This surprised me; I thought print would raise because None has no 'write' attribute. It's an 'is None' test, not a bool test; it does complain on sys.stdout = 0. It really doesn't like 0, it complains again -- about 'flush' -- on interpreter exit. :-)
There are lots of code out there where we need to protect things from being overwritten.
Perhaps you could give a more persuasive example. The whole idea is against the usual "consenting adults" approach of Python. Or as that famous Python programmer Barack Obama put it, "let's try not to do stupid shit." :-) Footnotes: [1] I suspect there's a race condition in some environments. At least up to version 9, MacOS poll(2) and X.org were buggy together. The event loop would poll(), and if you had an open file descriptor that hadn't been closed before fork but was somehow unused in the child, it would infloop on an OS error from poll(). So if you fork before sys.stdout gets GC'd and therefore .close()'d, you could end up with such a zombie fd.

On Wed, May 26, 2021 at 1:38 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Reply to Stestagg:
That's annotation to make sure no one uses a name.
No.
This is a proposal to implement constant name binding in Python.
Here are some excerpts from pep 591, specifically, how are they different from what you are proposing? [typing.Final purpose] "Declaring that a variable or attribute should not be reassigned" and " The two main rules for defining a final name are: - There can be *at most one* final declaration per module or class for a given attribute. There can't be separate class-level and instance-level constants with the same name. - There must be *exactly one* assignment to a final name. " and [typing.Final is useful in...]"Allowing a name to be used in situations where ordinarily a literal is expected (for example as a field name for NamedTuple, a tuple of types passed to isinstance, or an argument to a function with arguments of Literal type [3] <https://www.python.org/dev/peps/pep-0591/#pep-586>)."
There are many applications of constants.
However many there may be, the cases discussed here all fall under this definition

On 5/26/21 7:40 AM, Steven D'Aprano wrote:
So we only have to check at runtime if name has been marked constant to handle cases like: if flag: constant foo = ["List"] else: foo = ["Other"] or import mymodule constant mymodule.foo = ["List"] or are you going to restrict how constants are created?
He is assuming that people are familiar with C. -- Richard Damon

On Wed, May 26, 2021 at 9:20 PM Richard Damon <Richard@damon-family.org> wrote:
That's basically going to be impossible, especially since the object could reference OTHER objects which could change, too. Plus, it should be perfectly valid to say mylist.index(4) even if you can't do mylist.append(4), so you can't just guard against method calls. Enforcing immutability externally is basically impossible. Don't even bother trying. A more viable proposal might be "this name represents this literal", and should always be interpreted identically to that literal. So if you have that constant declaration, then every use of mylist is exactly equivalent to writing [1, 2, 3] at that exact point in the code (constructing a new list every time). That'd be well-defined and plausibly behaved, while still strongly recommending that immutables be used (since the compiler can constant-fold ints/floats/tuples/strings). The value would be that if you *edit* the constant (not rebind it, actually edit the source code), it would change in all places. I don't think it's of great value and I'm not recommending it, but it would at least be a sane and plausible proposal, without logical contradictions. ChrisA

Shreyan, you have probably spent hundreds, maybe thousands of words to avoid giving straightforward, direct, explicit answers to the questions, preferring to force us to guess what you mean from vague analogies. Please stop. Please give an explicit description of the functional requirements of this proposed feature. Give code samples showing the exact behaviour. Stop expecting us to guess what you mean when your statements contradict themselves. what happens when I try to rebind a constant? constant pi = 3.14 # later pi = 3.1415 For the record, this is not a rhetorical question. I do not know the answer, I have asked at least twice before what happens when we rebind a constant, *and you won't tell me*. On Wed, May 26, 2021 at 09:55:41AM -0000, Shreyan Avigyan wrote:
Literals mean 10 or 20. Can you assign something to literal? No.
Constants aren't literals, they are names. `10` is a literal, but `pi` is a constant. Can you assign to a name? Yes, otherwise we can't give the constant a value in the first place. Can you rebind that name? You have said yes. You wrote: "Constants doesn't mean we can't reassign the name to a different value." https://mail.python.org/archives/list/python-ideas@python.org/message/NTDZ4E... Actually, I think most people think that being a constant *does* mean that you can't reassign the name to a different value. Otherwise its not a constant, its just a variable. But in the same post I linked to above you explicitly ruled out classic constants like found in C and Java (and Pascal, and other languages). So now I have *no idea* what your constants are, since apparently they behave exactly like variables: they are names that refer to values, and you can rebind them to new values.
What do you mean by "change the value"? Are you talking about mutable objects? Earlier you said that we can change the name to point to a different value, now you say that we can't.
Simply think "const type *pointer" not "type *pointer const".
That means nothing to me. This is a proposal for a feature in Python, please describe it in terms of existing Python behaviour, not whatever language that is.
How would it be detectable? Please stop expecting us to guess what will happen. **Tell us what will happen.** It shouldn't be this hard to get information out of somebody proposing a new language feature :-( -- Steve

Reply to Steven - Sorry for creating confusions. 1. Leave debugging. As I said that's not a good argument for the existence of constants. 2. "Constants doesn't mean we can't reassign the name to a different value." was differently intended. I was trying to say that we should *treat* it like a *literal*. I never said constant will be a type of variable. I was a little bit unclear in that message. 3. Constant will be constant name binding. Value can be mutated (if mutable) but the name cannot be reassigned. 4. constant pi = 3.14 # later pi = 3.1415 # Error

On Wed, 26 May 2021 at 12:59, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Steven's already asked what error, and is it compile time or runtime. I'll add foo.py: constant x = 12 bar.py import foo foo.x = 19 You can only detect this at runtime. baz.py import foo name = 'x' setattr(foo, name, 22) You can only detect this at runtime. Also, if some_condition: constant a = 1 else: a = 2 a = 3 Is this allowed? Is a constant? What about for i in range(10): constant a = [] Is this allowed, or is it 10 rebindings of a? for i in range(10): constant a = [i] What about this? What is a "literal" anyway? You've used lists in examples (when discussing mutability) but list displays aren't actually literals. Lots of questions. I'm sure it's possible to answer them. But as the proposer, *you* need to give the answers (or at least give a complete and precise enough specification that people can deduce the answers). At the moment, everyone is just guessing. Paul

Reply to Paul Moore: if some_condition: constant a = 1 else: a = 2 a = 3 Yes this is allowed. This is runtime. for i in range(10): constant a = [] Not sure. Though it's preferable to be runtime. Preferable is "not allowed". And lists are also literals. Any Python Object that is not assigned to a variable is a literal. Python claims that itself. A preview - [10] = [2] SyntaxError: Can't assign to literal here. Constants should have a similar error - constant x = 10 x = [2] SomeErrorType: Can't assign to constant here.

On Wed, May 26, 2021 at 10:31 PM Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
The literal that you can't assign to here is "10". You're perfectly allowed to assign to a list display: [x, y, z] = range(3)
Is it a syntax error? Be VERY specific here. It makes a huge difference. Also, what about this: constant x = 10 def f(): x = 20 SyntaxError? Runtime error? Shadowing? This is important and cannot be brushed aside. ChrisA

On Wed, May 26, 2021 at 10:37:34PM +1000, Chris Angelico wrote:
The x inside the function is just a local variable. The x outside the function and the x inside it are in different namespaces and shouldn't clobber each other any more than variables in different namespaces do. Constants in statically typed languages are easy, because the compiler knows which names are constant at compile-time and can disallow assignments to those names without any runtime cost. Constants in dynamically typed languages like Python are hard, because we either have to severely restrict their use so that they can be determined at compile-time, which goes against the dynamic nature of the language, or else we have to slow down every assignment to check whether it is a constant or not. So the irony is that constants can speed up statically typed languages, but slow down dynamically typed ones. -- Steve

On Wed, May 26, 2021 at 11:15 PM Steven D'Aprano <steve@pearwood.info> wrote:
I'd kinda expect that too, but given the uncertainties in the rest of this thread, I wasn't certain. I'm fairly lost as to what the proposal even is, though. It seems to morph every time it gets poked. ChrisA

On Wed, 26 May 2021 at 13:13, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Reply to Paul Moore:
In Python terms, a constant is a name that binds itself to a value in memory and that name cannot bind itself to a different value now (unlike variables). The value can be mutated (if mutable) but the name cannot bind to a different value once it has bind itself to a value.
"value in memory" isn't Python terminology. Nor is "variable". Please point me to the exact sections in the language reference if you feel I'm wrong here. In particular, Python refers to "identifiers" and "names" - see https://docs.python.org/3/reference/lexical_analysis.html#identifiers ("Identifiers (also referred to as names) are described by the following lexical definitions.") On Wed, 26 May 2021 at 13:31, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
OK, so a=3 may raise an exception at runtime (depending on the value of some_condition). We need to decide exactly what exception would be used but I'm OK with that. It's specifically *not* a syntax exception, though, as that's a compile-time exception.
I don't know what you mean here. Are you saying that you'd prefer it not to be allowed? It's your proposal, the decision is up to you.
And lists are also literals. Any Python Object that is not assigned to a variable is a literal. Python claims that itself. A preview -
Read the language reference here: https://docs.python.org/3/reference/lexical_analysis.html#literals
[10] = [2] SyntaxError: Can't assign to literal here.
As Chris pointed out, this is flagging the assignment to the literal 10. Have you properly researched the existing semantics to make sure you understand what you're proposing to change, or are you just guessing?
But you just said it was runtime, so it definitely *isn't* similar to the syntax error "Can't assign to literal here". You're making inconsistent statements again :-( On Wed, 26 May 2021 at 13:53, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
However, there's a *huge* amount of extremely useful code out there that does assign to sys.stdout - pytest's mechanisms for capturing test output use this, for example. Making sys.stdout constant would break this. I don't think you've thought this proposal through at all, to be honest. You seem to be making up answers as the questions arise, which is *not* what people are asking for here. We are asking that you *explain* your proposal, assuming that you already know the answers and are simply struggling to communicate the details. Paul

Reply to Paul Moore:
That's exactly why I wrote SomeErrorType instead of SyntaxError. They are never similar. I just said the behavior would seem similar.
I'm trying my best. But sometimes there are few questions coming up that confuses and I'm not sure of the answer and I'm ending up making inconsistent statements.

On Wed, 26 May 2021 at 14:33, Shreyan Avigyan <pythonshreyan09@gmail.com> wrote:
Your precise comment was
See above? You said "Constants should have a similar error" and then in your response to my reply, you said "That's exactly why I wrote SomeErrorType instead of SyntaxError. They are never similar. I just said the behavior would seem similar." How am I to interpret that? Your statements are directly contradictory ("similar error" vs "never similar"). And for information, I personally don't find compile time and runtime exceptions to "seem similar" at all, especially in the context of this discussion where we've repeatedly tried to get you to decide whether you're talking about compile time or runtime.
I'm trying my best. But sometimes there are few questions coming up that confuses and I'm not sure of the answer and I'm ending up making inconsistent statements.
So stop replying instantly, read the feedback, think your proposal through, and when you have an updated proposal that actually addresses all of the flaws people have pointed out with your current proposal, post that (with a clear explanation of what you changed and why). Knee-jerk replies from you aren't respectful of the time people are putting into trying to follow what you're proposing :-( Paul PS I know there's a tendency for people to reply very quickly on this list, and I know that can leave people feeling under pressure to reply quickly themselves. But it's a false pressure - some people simply need time to digest feedback, others are having to handle a language they are not a native speaker of, still others may have other reasons for needing extra time. If you're in a situation like that, please *do* take that time. We won't complain if you take time to compose your answers.

On Wed, May 26, 2021 at 12:31:07PM -0000, Shreyan Avigyan wrote:
Do you have any suggestions for how this should be implemented, how the interpreter will know at runtime whether or not the name `a` is a constant?
That is equivalent to: constant a = [] constant a = [] constant a = [] # seven more times If you can't rebind constants, then that better be disallowed. Otherwise you have rebound the constant `a` nine times: initialised it to one list on the first loop, and then the next nine loops you bind the same name to nine different lists.
And lists are also literals.
Lists are not literals. The documentation calls them *displays*. You won't find lists under "literals": https://docs.python.org/3/reference/expressions.html#literals but you will find them here: https://docs.python.org/3/reference/expressions.html#list-displays I admit that I too often wrongly refer to expressions like `[1, 2]` as a list literal, but that language is not technically correct for Python.
Any Python Object that is not assigned to a variable is a literal.
That's incorrect. When I write the expression: print(a*x + b) the object returned by `a*x + b` is not assigned to anything, but it's not a literal.
You are misinterpreting the error. If you look at the pointer in the syntax error, it points at the 10, not the list: >>> [10] = [2] File "<stdin>", line 1 [10] = [2] ^ SyntaxError: cannot assign to literal That statement is doing sequence unpacking: it unpacks the list [2], and the target list [10], and tries to assign 10 = 2 which is forbidden because 10 is a literal, *not* because `[10]` is a literal. (It isn't.) You can see a better example here: >>> [a, b, c, 10, d, e] = range(6) File "<stdin>", line 1 [a, b, c, 10, d, e] = range(6) ^ SyntaxError: cannot assign to literal -- Steve

if you want it for debugging, then static type checkers can do that for you. 2) This would allow another branch of OOP programming to enter Python.
Read-only member variables. There are Private members in Java and C++.
Then use Java or C++ -- the benefits of Python are not primarily the syntax, but it's dynamic nature. Though this does bring up an interesting (to the ignorant of most things typing me) question: Do the static type checkers support the concept of private attributes? That might be a nice feature. -CHB ----- Chris Barker Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

Hello, On Tue, 25 May 2021 11:53:20 -0000 "Shreyan Avigyan" <pythonshreyan09@gmail.com> wrote:
The idea of introducing constants on the core language level in Python is well-known topic and was brought up on multiple occasions both on the python-ideas and python-dev mailing lists. A random example: https://www.mail-archive.com/python-dev@python.org/msg110424.html "constants in Python: Starting simple and gradually adding more features, was: Re: Pattern Matching controversy" The matter is actually implementing it in different Python implementations. And some implementations had support for *some kind* of constants for many years (e.g. MicroPython with it's pre-annotation syntax of "FOO = const(1)"), while CPython still has it only on the level of external first-generation annotation module, "typing". As another example, I can give "strict mode" (https://www.mail-archive.com/python-ideas@python.org/msg25403.html) feature of my Python dialect, Pycopy (https://github.com/pfalcon/pycopy), which implements some (but again, not all) aspects of const'ness. E.g.: === print("In import-time") a: const = 1 a: const = 2 def __main__(): print("In run-time") global a a = 3 === Gives: ==== In import-time Warning: strict mode: overriding (monkey-patching) const name 'a' In run-time Traceback (most recent call last): File "strict.py", line 9, in __main__ RuntimeError: strict mode: cannot override const name ==== -- Best regards, Paul mailto:pmiscml@gmail.com

Now it seems Python doesn’t need constant. There are many ways we can achieve constants. Though constants may increase performance. Yet again it can also be the opposite. ________________________________ From: Paul Sokolovsky <pmiscml@gmail.com> Sent: Saturday, May 29, 2021 12:44:09 AM To: Shreyan Avigyan <pythonshreyan09@gmail.com> Cc: python-ideas@python.org <python-ideas@python.org> Subject: [Python-ideas] Re: Introduce constants in Python (constant name binding) Hello, On Tue, 25 May 2021 11:53:20 -0000 "Shreyan Avigyan" <pythonshreyan09@gmail.com> wrote:
The idea of introducing constants on the core language level in Python is well-known topic and was brought up on multiple occasions both on the python-ideas and python-dev mailing lists. A random example: https://www.mail-archive.com/python-dev@python.org/msg110424.html "constants in Python: Starting simple and gradually adding more features, was: Re: Pattern Matching controversy" The matter is actually implementing it in different Python implementations. And some implementations had support for *some kind* of constants for many years (e.g. MicroPython with it's pre-annotation syntax of "FOO = const(1)"), while CPython still has it only on the level of external first-generation annotation module, "typing". As another example, I can give "strict mode" (https://www.mail-archive.com/python-ideas@python.org/msg25403.html) feature of my Python dialect, Pycopy (https://github.com/pfalcon/pycopy), which implements some (but again, not all) aspects of const'ness. E.g.: === print("In import-time") a: const = 1 a: const = 2 def __main__(): print("In run-time") global a a = 3 === Gives: ==== In import-time Warning: strict mode: overriding (monkey-patching) const name 'a' In run-time Traceback (most recent call last): File "strict.py", line 9, in __main__ RuntimeError: strict mode: cannot override const name ==== -- Best regards, Paul mailto:pmiscml@gmail.com _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@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/PHU3YE... Code of Conduct: http://python.org/psf/codeofconduct/
participants (17)
-
2QdxY4RzWzUUiLuE@potatochowder.com
-
Abdur-Rahmaan Janhangeer
-
Chris Angelico
-
Christopher Barker
-
Damian Shaw
-
David Mertz
-
Ethan Furman
-
MRAB
-
Paul Moore
-
Paul Sokolovsky
-
Richard Damon
-
Rob Cliffe
-
Shreyan Avigyan
-
Shreyan Avigyan
-
Stephen J. Turnbull
-
Stestagg
-
Steven D'Aprano