[HN Gopher] The PEPs of Python 3.9 ___________________________________________________________________ The PEPs of Python 3.9 Author : zdw Score : 172 points Date : 2020-05-28 15:02 UTC (7 hours ago) (HTM) web link (lwn.net) (TXT) w3m dump (lwn.net) | nemetroid wrote: | > Another kind of clean up comes in PEP 585 ("Type Hinting | Generics In Standard Collections"). It will allow the removal of | a parallel set of type aliases maintained in the typing module in | order to support generic types. For example, the typing.List type | will no longer be needed to support annotations like "dict[str, | list[int]]" (i.e.. a dictionary with string keys and values that | are lists of integers). | | I think this will go a long way toward making type annotations | feel less like a tacked-on feature. | diarrhea wrote: | Looking "back" now, it never occurred to me that importing List | when there is list is particularly strange. Now it sticks out | sorely. Very glad this change is happening. | mixmastamyk wrote: | There have been so many improvements in the typing area, I wish | they'd waited a while to get it right. | gorgoiler wrote: | I was quite surprised -- only a few days ago in fact -- to | discover the standard Python library has no support for Olson (as | in _tzdata_ ) timezones. Time arithmetic is impossible without | them. | | The ipaddress library also has no support for calculating | subnets. It is quite hard to go from 2a00:aaaa:bbbb::/48 to | 2a00:aaaa:bbbb:cccc::/64. It would be less weird if the essence | of the documentation didn't make it sound like the library was | otherwise very thorough in the coverage of its implementation. | | Can anyone write a PEP? Maybe I should get off my behind and | actually submit a patch for proper IP calculations? Or maybe I | missed it in the documentation (which, aside, I wish wasn't | written with such GNU-info style formality.) | chc wrote: | Unless I misunderstand what you're looking for, I think that | functionality is in there. original_net_48 = | ip_network("2a00:aaaa:bbbb::/48") desired_subnet = | ip_network('2a00:aaaa:bbbb:cccc::/64') subnets_64 = | original_net_48.subnets(16) print(f"{desired_subnet} is | one of the computed subnets: {desired_subnet in subnets_64}") | #=> 2a00:aaaa:bbbb:cccc::/64 is one of the computed subnets: | True | gorgoiler wrote: | Thanks, but your second line kind of has the answer in it | already. It's more like... site = | ip_network('2a00:aaaa:bbbb::/48') subnet = f(site, 64, | 0xcccc) | | ...and I don't think _f()_ is in the standard library. But | maybe I just index the calculated subnets, from your example? | I'll give it a go! | | Edit: yes! But it's a bit slow... | | https://repl.it/repls/PoshPapayawhipNaturaldocs#main.py | sigjuice wrote: | The ipaddress library doesn't do link-local IPv6 addresses | either. | heavyset_go wrote: | Am I the only one who wants multi-lined anonymous functions in | Python? I find myself really wanting to reach for arrow functions | sometimes while writing Python, and end up disappointed that they | aren't available. | dragonwriter wrote: | > Am I the only one who wants multi-lined anonymous functions | in Python? | | Lots of people want them (lots of people don't, too), but no | one has come up with a great syntax that plays nice with the | rest of Python and saves you much over named functions. | fabatka wrote: | I think the basic idea is "if you need multiple lines,you | should declare a proper fuction", so I wouldn't stand on one | foot until multiline anonymous functions in python. | trashburger wrote: | I believe Guido was against it as he is mostly opposed to the | functional style, as a matter of fact he was opposed to lambas | but begrudgingly added then after many requests. | quietbritishjim wrote: | What's wrong with using a nested named functions instead? | | You may already be aware, but not everyone is: they capture | variables from outer scopes in exactly the same way that | lambdas do. | tln wrote: | Sometimes I wish that python strings weren't directly iterable... | | this article sums it up better than I ever could | https://www.xanthir.com/b4wJ1 | | ...then str.strip and variants could be cleanly and logically | extended to allow this functionality, because passing a string | and a sequence of strings would be distinguishable. | | Alas, clean and logical function design can be hard to do late in | a languages life. | | PEP 593 and PEP 585 are clean and logical... glad to see that :) | joshuamorton wrote: | On non-iterable strings: The recursive type problem can be | solved, with something like what is proposed in [0]. (I have an | implementation of a fix on github linked from that thread, | there's edge case fixes and PEP's scare me, but technically | it's feasible). | | [0]: https://mail.python.org/archives/list/typing- | sig@python.org/... | spott wrote: | While that may solve the recursive type problem, it doesn't | really solve the "iterating over strings is rarely what you | actually want to do" problem. | jorams wrote: | I agree with most of the article you link, but there's one | thing I don't understand: The article quickly dismisses the | obvious fix for recursive iterability, to make strings be | composed of "characters": | | > And an obvious "fix" for this is worse than the original | problem: Common Lisp says that strings are composed of | characters, a totally different type, which doesn't implement | the same methods and has to be handled specially. It's really | annoying. | | It seems to me this contradicts most of what the article says. | Sure, strings are rarely collections, so they should not be | iterable by default. But the final solution offered admits that | sometimes they are, and then you want to be able to iterate | over _something_. For most instances of _something_ , It does | not make sense for the individual "elements" to be strings. | Bytes are clearly not strings, code points are clearly not | strings, grapheme clusters are clearly not strings. Each of | those will provide very different methods, because they are | very different things. Only after that point (words, sentences, | etc.) does the idea of the element being the same type start | making sense again. | | Clearly the concept of a "character" is too ambiguous, and | there is no clear "default" for what it should mean, but the | idea of a string consisting of some kind of element that is not | string appears obviously correct. | BiteCode_dev wrote: | You can easily distinguish them: if | isinstance(msg, str) | | So I don't think that's a good argument for not accepting | iterables of strings in str methods. Things like replace() | would benefit a lot and it's not that hard to do, you can even | accept regexes optionally: https://wonderful- | wrappers.readthedocs.io/en/latest/string_w... | | I agree that iterating on string is not proper design however. | It's not very useful in practice, and the O(1) access has other | performance consequences for more important things. | | Swift did it right IMO, but it's a much younger language. | | I also wish we stole the file api concepts from swift, and that | open() would return a file like object that always gives you | bytes. No "b" mode. If you want text, you to open().as_text(), | and get a decoding wrapper. | | The idea that there are text files and binary files has been | toxic for a whole generation of coders. | Skunkleton wrote: | > The idea that there are text files and binary files has | been toxic for a whole generation of coders. | | It really is nonsense isn't it? Its like asking a low level | api for opening files as a .doc, or as a pdf. Why would that | be part of the file io layer? | aidos wrote: | Well, I guess it's easy to argue that it's so common that | beginners would expect to open files as text. You can see | how it would evolve that way. | | Now I'm more familiar with it I'm careful to be explicit | with the decoding when using text to make it super obvious | what's going on. | signal11 wrote: | I suspect it's also to do with Python's history as a | scripting language. Because of Perl's obvious strengths | in this area, any scripting language pretty much _has_ to | make it very easy to work with text files. Ruby does | something similar for instance. | | Even languages like Java now recognise the need to | provide convenient access to text files as part of the | standard API, with Files.readAllLines() in 7, | Files.lines() in 8, and Files.readString() in 11. | heavenlyblue wrote: | My first mistake I made as a beginner was dumping a bunch | of binary data as text. Something would happen in the way | and not the whole data would be written because I was | writing it in text mode. | | It just never appeared to me that the default mode of | writing the file would _not_ write the array I was | passing it. | | It's much more important for beginners to be able to | learn clear recipes rather than having double standards | with a bunch of edge cases. | aidos wrote: | I've done worse. Using MySQL from php and not having the | encoding right somewhere along the way so all my content | was being mojibaked on the way in and un-mojibaked on the | way out so I didn't notice it until deep into a project | when I needed to extract it to another system. | | EDIT thanks, I knew that didn't look quite right. | "Mojibaked" - such a great term. | mark-r wrote: | The term is actually "Mojibake", not "emoji baked". | https://en.wikipedia.org/wiki/Mojibake#Etymology | diarrhea wrote: | The issue is that if isinstance(msg, str) | | will clutter code that is otherwise clean. A single type has | to be specially handled, which sticks out like a sore thumb. | | As a second point, do you have more on your last sentence? | ("The idea that there are text files and binary files has | been toxic for a whole generation of coders."). I have been | _thoroughly_ confused about text vs. bytes when learning | Python /programming. | | The two types are treated as siblings, when text files are | really a child of binary files. Binary files are simply | regular files, and sit as the single parent, without parents | itself, in the tree. Text files are just one of the many | children, that happen to yield text when their byte patterns | happen to be interpreted using the correct encoding (or, in | the spirit of Python, decoding when going from bytes to | text), like UTF8. This is just like, say, audio files | yielding audio when interpreted with the correct encoding | (say MP3). | | Is this a valid way of seeing it? I have to ask very | carefully because I have never seen it explained this way, so | that is just what I put together as a mental model over time. | In opposition to that model, resources like books always | treat binary and text files as polar opposites/siblings. | | This leads me to the initial question of whether you know of | resources that would support the above model (assuming it is | correct)? | BiteCode_dev wrote: | The open() API is inherited from the C way, where the world | is divided between text files and binary files. So you open | a file in "text" mode, and "binary" mode, "text" being the | default behavior. | | This is, of course, utterly BS. | | All files are binary files. | | Some contains sound data, some image data, some zip data, | some pdf data, and some raw encoded text data. | | But we don't have a "jpg" mode for open(). We do have | higher API we pass file objects to in order to decode their | content as jpg, which is what we should be doing to text. | Text is not an exceptional case. | | VSCode does a lot of work to turn those bytes into pretty | words, just like VLC into videos. They are not like that in | the file. It's all a representation for human consumption. | | The reasoning for this confusing API is that reading text | from a file is a common use, which is true. Espacially on | Unix, from which C is from. But using a "mode" is the wrong | abstraction to offer it. | | If fact, Python 3 does it partially right. It has a | io.FileIO object that just take care of opening the stuff, | and a io.BufferedReader that wraps FileIO to offer | practical methods to access its content. | | This what what open(mode="b") returns. | | If you do open(mode="t"), which is the default, it wraps | the BufferedReader into a TextStream that does the decoding | part transparently for you, and returns that. | | There is an great explanation of this by the always | excellent David Beazley: | http://www.dabeaz.com/python3io_2010/MasteringIO.pdf | | What it should do is offering something this: | with open('text.txt').as_text(): | | open() would always return BufferedReadfer, as_text() would | always return TextStream. | | This completly separates I/O from decoding, removing | confusion in the mind of all those coders that would | otherwise live by the illusionary binary/text model. It | also makes the API much less error prone: you can easily | see where to the file related arguments go (in open()) and | where to text related arguments go (in as_text()). | | You can keep the mode, but only for "read", "write" and | "append", removing the weird mix with "text" and "bytes" | which are really related to a different set of operations. | VWWHFSfQ wrote: | How would this work with | open('text.txt', 'w').as_text(): | BiteCode_dev wrote: | with open('text.txt','w').as_text() as f: | f.write("text") | viraptor wrote: | That sounds completely like a correct way to look at it. | I'd put "stream of bytes" and "seekable stream of bytes" | above files, but that's just nitpicking. | | For me the toxic idea about text files is that they're a | thing at all. They're just binary files containing encoded | text, _without_ any encoding marker making them an ideal | trap. Is a utf16 file a text file? Is a shift-jis file a | text file? Have fun guessing edge cases. We 've already | accepted with unicode that the "text" or letters are | something separate from the encoding. | ghshephard wrote: | Totally agree that everything should be a byte stream. | Even with Python 3.x text files are still confusing - if | you open a UTF-8 file with a BOM in the front as a text | file - should that BOM be part of the file contents, or | transparently removed? By default, Python treats it as | actual content, which can screw all sorts of things up. | In my ideal world, _every file_ is a binary file, and | that if you want it to be a text file - just open it with | whatever encoding scheme you think appropriate (typically | UTF-8). | | If you don't know the Encoding? Just write a quick | detect_bom function (should be part of the standard | library, no idea why it isn't) and then open it with that | encoding. I.E.: encoding = | detect_bom(fn) with open (fn, 'r', encoding = | encoding) as f: ... | | That also has the benefit of removing the BOM from your | file. | | Ultimately, putting the responsibility for determining | the CODEC on the user at least makes it _clear_ to them | what they are doing -opening a binary file and decoding | it. That mental model prepares them for the first time | they run into, say, a cp587 file. | | I understand why Python doesn't do this - it adds a bit | of complexity - though you could have an "auto-detect" | encoding scheme that tried to determine the encoding | schemes, and defaults to UTF-8 - not perfect, as you | can't absolutely determine the CODEC of a file by reading | it - but better than what we have today - where your code | crashes when you have a BOM that upsets UTF-8 decoder. | | I finally wrote a library function to guess codecs and | read text files, inspired by | https://stackoverflow.com/a/24370596/1637450 and haven't | been tripped up since. | | But Python does _not_ make it easy to open "text" files | - and I know data engineers who've been doing this for | years who are still tripped up. | BiteCode_dev wrote: | Chardet, written by mozilla, already detect encoding if | you need such thing. | danpalmer wrote: | Strings being it stable can cause problems, and another | commenter has pointed out that Swift handles it well. | | However I think strings being iterable is one of the core | ergonomics in the language and basic types of Python that make | it so nice for many applications. Scripting, scraping, data | cleanup, data science, even basic web development, all benefit | hugely from little features like this. Without this sort of | thing Python would be a different language with different uses. | | While I normally like safety and types, I'm personally happy | with things like this because it fits with Pythons strengths. | cauthon wrote: | I disagree, I don't think there's any meaningful benefit. For | example, lets say we iterated over strings as follows. | | for char in my_str.chars(): foo() | | That wouldn't sacrifice any ergonomics, being consistent with | how we already iterate over dictionary contents with | d.items(), and it'd address all the concerns in the parent | comment link | slightwinder wrote: | > Eventually, removeprefix() and removesuffix() seemed to gain | the upper hand, which is what Sweeney eventually switched to. | | Great naming...missed their chance to make the functionality of | strip/lstrip/rstrip more clear by name the new methods | stripword/lstripword/rstripword. Which would also had the benefit | of consistence. | masklinn wrote: | stripwords could (/would) imply that it acts on words. As in, | whitespace separated things. | trashburger wrote: | I welcome the terser type hints for generics. I was wishing for | something terser, like: {str: [int]} | | being equivalent to what is currently dict[str, list[int]] in the | PEP, but I guess it will have to do. | jefft255 wrote: | Could someone explain to me what kind of new language features | the new parser will allow? I'm curious and very incompetent when | it comes to understanding what LL(1) grammar would imply for the | end-user (the python programmer like me). | MHordecki wrote: | The linked LWN article[1] mentions context-sensitive keywords, | ie. a way to treat certain words as language keywords only in | specific contexts. For example, a new match statement that | wouldn't require reserving the `match` word as a language | keyword, which would require a breaking change and break all | existing code that uses `match` as a variable name. | | Such a feature requires support from the parser. | | [1]: https://lwn.net/Articles/816922/ | kalenx wrote: | One good example (for those who do not want to read the full | article) is the async keyword. Introducing it as keyword | broke a few libraries which were already using them as kwarg | in some functions (e.g. pytorch). | saagarjha wrote: | I wonder if Python will go back and address other shortcoming | which I assume are tied to the parser, such as the inability | to use quotes inside the interpolated segments. | pixelmonkey wrote: | Extensive explanation from Python creator and PEG parser | implementor Guido van Rossum himself can be found in this | video: | | https://youtu.be/QppWTvh7_sI | | It's also just a fun video on language parsers in general. | wenc wrote: | I used a PEG parser for a language I designed because I was | attracted to the linear time parsing achievable using a | packrat parser. | | However, I also found that parsing is rarely a performance | bottleneck so that wasn't a big plus. | | PEGs are however are easier to reason about, no ambiguities, | so that was a good enough reason. | haberman wrote: | > PEGs are however are easier to reason about, no | ambiguities, so that was good enough reason. | | I frequently hear this mentioned ("PEGs don't have | ambiguity"). It is literally true, but I don't think it's | true in the sense that actually matters. | | I've blogged about this in the past | (https://blog.reverberate.org/2013/09/ll-and-lr-in-context- | wh...), but I'm not the only person saying this: | | > PEG is not unambiguous in any helpful sense of that word. | BNF allows you to specify ambiguous grammars, and that | feature is tied to its power and flexibility and often | useful in itself. PEG will only deliver one of those | parses. But without an easy way of knowing which parse, the | underlying ambiguity is not addressed -- it is just | ignored. | | https://jeffreykegler.github.io/Ocean-of-Awareness- | blog/indi... | justinpombrio wrote: | > PEGs are however easier to reason about | | Yes and no. | | PEG parsers are definitely easier to _implement_ than any | of the LR(k)-and-ilk parsers. So if you 're writing or | debugging a PEG parser, that will be easier. | | However, while shift-reduce conflicts are confusing, they | are there to give the strong guarantee that the grammar is | unambiguous. And the parser generator will tell you this as | soon as you've defined the grammar, before you've even used | it. PEG grammars instead remove the guarantee, and let you | deal with any confusion that arises much later. | | Here are some methods of reasoning that standard parsers | will give you that PEG parers will not: | | 1. A ::= B | C is exactly the same as A ::= C | B. | | 2. If A ::= B | C and you have a program containing a | fragment that parses as an "A" because it matched "B", then | you can replace that fragment with something that matches | "C" and it the program will still parse. | | Neither of these rules hold in PEGs. | | Here's a practical concern that (2) helps with. Say you | have a grammar for html, and a grammar for js. And you want | to be able to parse html with embedded JS. So you stick the | js grammar into the html grammar at the right places. If | you're using a standard (e.g. LR(k)) parser, and you _don | 't_ get any shift-reduce (or other) conflicts, then the | combined grammar works. In contrast, if you're using a PEG | grammar, it's possible that you've ordered things wrong and | there are valid JS programs that will never parse because | they're clobbered by html parsing rules outside of them. Or | vice-versa. | | Also, realistically if you're using a PEG parser you'll | want one that handles left recursion, because working | without left recursion turns your grammar into a mess. And | left recursion in PEGs can have some weird behavior. | 1wd wrote: | https://pyfound.blogspot.com/2020/04/replacing-cpythons-pars... | gives "Parenthesized with-statements" as an example. | OJFord wrote: | > Eric Fahlgren amusingly summed up the name fight this way: | | > > I think name choice is easier if you write the documentation | first: | | > > cutprefix - Removes the specified prefix. | | > > trimprefix - Removes the specified prefix. | | > > stripprefix - Removes the specified prefix. | | > > removeprefix - Removes the specified prefix. Duh. :) | | I actually don't agree that it's so obvious, since it returns the | prefix-removed string rather than modifying in-place. I think | Fahlgren's argument would work better for `withoutprefix`. | theandrewbailey wrote: | Python strings are immutable, so in-place modification would | violate lots of rules and conventions. | kbd wrote: | I would have preferred 'stripprefix' for unity with 'strip', | 'rstrip', and 'lstrip' | mixmastamyk wrote: | They work differently. Strip removes all given characters | from the end in any order. Trim sounds better to me, like one | operation. | pdonis wrote: | That's discussed in the article: the "strip" methods don't | interpret strings of multiple characters as a single prefix | or suffix to be removed, so it was felt to be too confusing | to use "strip" type names for methods that _do_ interpret | strings that way. | thijsvandien wrote: | Ugh, I do like withoutprefix a lot better. That makes it | obvious it returns something new and that there's no reason to | raise ever. | BiteCode_dev wrote: | Strings are immutable in Python, and all strings operations | return new strings, including all string methods. | | So there is no possible confusion. | ianhorn wrote: | If I have a string x = "this is a very long string..." and do | y = x[:10], then it's a whole new string? If x is near my | memory limits, and I do y = x[:-1] will it basically double | my memory usage? Is that what you meant by every string is a | new string? | Erlangen wrote: | Slicing in Python always create a new object. You can test | it with a list of integers.. | masklinn wrote: | > Slicing in Python always create a new object. | | It always creates a new object but it doesn't necessarily | copy the contents (even shallowly). | | For instance slicing a `memoryview` creates a subview | which shares storage with its parent. | cecilpl2 wrote: | My favorite example of something similar to this, since | you brought it up: >>> a = [254, 255, | 256, 257, 258] >>> b = [254, 255, 256, 257, 258] | >>> for i in range(5): print(a[i] is b[i]) ... | True True True False False | | In Python, integers in the range [-5, 256] are statically | constructed in the interpreter and refer to fixed | instances of objects. All other integers are created | dynamically and refer to a new object each time they are | created. | ianhorn wrote: | It'll always create a new object but my understanding is | that at least in numpy the new and old object will share | memory. Am I wrong there too? | dialamac wrote: | CPython is pretty terrible. Numpy has the concept of | views, cpython doesn't do anything sophisticated. | aidos wrote: | Correct. In Numpy the slices are views on the underlying | memory. That's why they're so fast, there's no copying | involved. Incidentally that's also why freeing up the | original variable doesn't release the memory (the slices | are still using it). | masklinn wrote: | > If I have a string x = "this is a very long string..." | and do y = x[:10], then it's a whole new string? | | Yes. And doing otherwise is pretty risky as the Java folks | discovered, ultimately deciding to revert the optimisation | of substring sharing storage rather than copying its data. | | The issue is that while data-sharing substringing is | essentially free, it also _keeps the original string alive_ | , so if you slurp in megabytes, slice out a few bytes you | keep around and "throw away" the original string, that one | is still kept alive by the substringing you perform, and | you basically have a hard to diagnose memory leak due to | completely implicit behaviour. | | Languages which perform this sharing explicitly -- and | especially statically (e.g. Rust) -- don't have this issue, | but it's a risky move when you only have one string type. | | Incidentally, Python provides for opting into that | behaviour for _bytes_ using memory views. | [deleted] | sadfklsjlkjwt wrote: | Yes. | ORioN63 wrote: | Yes. Although you can use `islice` from itertools to get | around this problem, when a problem. | BiteCode_dev wrote: | If x is near your memory limits, and you do y = x[:-1], you | will get a MemoryError :) | | For those situations, bytes() + memoryview() or bytearray() | can be used, but then you are on your own. | ianhorn wrote: | Huh, I've had a wrong understanding of that for over a | decade! TIL, thanks. | BiteCode_dev wrote: | Hey! | | https://xkcd.com/1053/ | | And honestly, I would be rich if I got a dollar every | time a student does this: msg.upper() | | Instead of: msg = msg.upper() | | And then call me to say it doesn't work. | pbowyer wrote: | > And honestly, I would be rich if I got a dollar every | time a student does this: | | > msg.upper() | | > Instead of: | | > msg = msg.upper() | | > And then call me to say it doesn't work. | | On this, isn't the student's reasoning sensible? E.g. "If | msg is a String object that represents my string, then | calling .upper() on it will change (mutate) the value, | because I'm calling it on itself"? | | If the syntax was upper(msg) or to a lesser extent | String.upper(msg) then the new-to-programming me would | have understood more clearly that msg was not going to | change. Have you any insights into what your students are | thinking? | BiteCode_dev wrote: | A student don't know anything about mutability, and since | Python signatures are not explicit, there is no way to | know they have to do that. | | It's just something to be told. A design decision, like | there are thousands to learn in IT, that you just can't | guess. | cesarb wrote: | IMO, this is a defect in the language: the lack of a | "must_use" annotation or similar. If that annotation | existed, and the .upper() method was annotated with it, | the compiler could warn in that situation. | diarrhea wrote: | But you are free to do if title == | user_input.upper(): | | That is, you convert a string to upper without binding | the result to a name. You just use it in-place and | discard the result, which is fine. | | With compiler, you mean mypy or linters? | TkTech wrote: | That's still "using" the resulting value for a | comparison. CPython isn't an optimizing compiler, or it | would completely remove the call to upper(). | >>> def up(v): ... v.upper() ... | >>> dis.dis(up) 2 0 LOAD_FAST | 0 (v) 2 LOAD_METHOD 0 | (upper) 4 CALL_METHOD 0 | 6 POP_TOP 8 LOAD_CONST | 0 (None) 10 RETURN_VALUE | >>> def up(v): ... if v.upper() == | "HelloWorld": ... return True ... | >>> dis.dis(up) 2 0 LOAD_FAST | 0 (v) 2 LOAD_METHOD 0 | (upper) 4 CALL_METHOD 0 | 6 LOAD_CONST 1 ('HelloWorld') | 8 COMPARE_OP 2 (==) 10 | POP_JUMP_IF_FALSE 16 3 12 | LOAD_CONST 2 (True) 14 | RETURN_VALUE >> 16 LOAD_CONST | 0 (None) 18 RETURN_VALUE | | Notice in the first example, right after CALL_METHOD the | return value on the stack is just immediately POP'd away. | The parent is saying that when you run `python | example.py` CPython should see that the return value is | never used and emit a warning. This would only happen | because `upper()` was manually marked using the suggested | `must_use` annotation. | delaaxe wrote: | He meant that writing a line of code with only contents: | msg.upper() | | should trigger a warning as this clearly doesn't do | anything. | BiteCode_dev wrote: | Python is interpretted, not compiled, and completly | dynamic. You cannot check much statically. | | In fact, any program can replace anything on the fly, and | swap your string for something similar but mutable. | | It's the trade off you make when choosing it. | [deleted] | theandrewbailey wrote: | No PEP 554 (subinterpreters). That's been moved to 3.10: | https://www.python.org/dev/peps/pep-0554/ | BiteCode_dev wrote: | Given how heated was the debate about those, it's good we | didn't try to go too fast with it. | | I'm full of hopes for this feature, but it's going to be a slow | hard work, and we'll only rip the benefit on the long run. So | no use to rush it. | | I feel like we rushed asyncio and type hints, and it took years | to make them usable after they were introduced. | traes wrote: | Is the next release still planned to be called Python 4? I seem | to recall GvR saying that at one point, though I could be | mistaken. | BiteCode_dev wrote: | 3.10. | | https://www.python.org/dev/peps/pep-0619/ | twic wrote: | After that will be Python 3.11, and after that, Python for | Workgroups 3.11. | cozzyd wrote: | Followed by inexplicably jumping to pithon. | doersino wrote: | Python 95 is gonna blow everyone's minds. | BiteCode_dev wrote: | With semver, it's going to be too long to reach. I | suggest we switch to the Firefox versioning scheme, it | seems to already be near this goal. | agumonkey wrote: | the first release with Plug and Pep | _jal wrote: | Yeah, but PythoNT will be around for a long time. | mixmastamyk wrote: | Start me up! | pansa2 wrote: | You make a grown man cry... | downerending wrote: | And very importantly, would Python 4 be a new language, or | compatible with Python 3? (compare Python 3 vs Python 2) | Scarblac wrote: | They decided they didn't like the backwards-incompatible | changes they made in 3, so Python 4 will go back to how | things were in 2. | BiteCode_dev wrote: | There is no Python 4 planned for now. | | Python broke compat once in 25 years and gave 13 years to | migrate. | | It's a very conservative language. | mixmastamyk wrote: | Python breaks compatibility often on minor point releases. | Only once as big as 3.0.0 but it happens regularly. | | I argued on the list that these should be kept for major | releases for planning reasons but they appear to be | convinced it is too hard. | kzrdude wrote: | If we look back in python history, the rolling breaking | changes have been handled mostly fine, and the actual | Python 3 caused a lot of pain in the ecosystem. So I hope | they stay away from major versions and keep up the other | things they are doing. | mixmastamyk wrote: | That was due to the scope of the breakage, not number | format. A good way to handle that and maintain | predictability is to constrain breaking changes, yet | defer them to 4.X. | ghshephard wrote: | Can you provide an example of where Python has broken | backwards compatibility recently between 3.x version? | I'll admit (despite googling for 5 or so minutes) that i | don't actually know if it does. It obviously breaks | _forward_ compatibility continuously all the time - new | language features are landing, and they just aren 't | present in previous versions - but I don't know if I've | ever run into people being tripped up by that. | | I know some Python _Libraries_ break backwards | compatibility (Pandas being a big one) - but, for the | most part, hasn 't the language been backwards compatible | since at least Python 3.4? (And possibly further back, | for all I know). | mixmastamyk wrote: | On this page you'll see them. Check the "Porting to X.X" | items under each release: | | https://docs.python.org/3/whatsnew/index.html | | Keep in mind they have deferred a number of them because | of the impending EOL of Python 2.7. There are fewer | breaking changes during the latter 3.X series, which | should resume in 3.9 or 3.10 now that Python2 has passed | on. | | Here's a commonly mentioned one: | | Changes in Python Behavior: async and await names are now | reserved keywords. Code using these names as identifiers | will now raise a SyntaxError. (Contributed by Jelle | Zijlstra in bpo-30406.) | | Note: I think this is a bad idea, I'd rather all these | small breaking changes and parser be deferred to 4.X. But | they need to be _small breaking changes,_ of course, not | a new language. | downerending wrote: | I'm struggling to think of any other language that has done | something like this. | | It might seem like a quibble, but it seems better to | describe Python 3 as a different language versus Python 2. | Newbies seem to get that. | | (Or, alternatively, "How many Python 2 scripts will run on | a Python 3 interpreter?" Answer: "None of them.") | vasco wrote: | PHP which tried to address unicode in version 6, but then | abandoned it and went straight to 7. Perl, which | amusingly also at version 6 decided on a huge re-write, | but then just decided to rename the version as an actual | new language "Raku". | downerending wrote: | Re _Raku_ , in hindsight, this was the right call. Give a | new language a new name, and the story stays simple. | The_Colonel wrote: | Yep. Also people are not shamed and ridiculed online | because they did not upgraded to new language. | BiteCode_dev wrote: | It if was such a righ call, why did it die ? | nostoc wrote: | > Or, alternatively, "How many Python 2 scripts will run | on a Python 3 interpreter?" Answer: "None of them." | | That is completely false. So many libraries have support | both for 2 and 3. That's code that run just as well under | both interpreter. | downerending wrote: | Really? The same .py file runs under _python2_ and | _python3_? | | Googling quickly, I find this, which does a bit better | than 2to3. I suppose one could write to a somewhat | constrained intersection of Python2 and Python3, if one | is willing to make at least some boilerplate changes to | the original Python2 code. | | https://python-future.org/overview.html | | That said, if you bring a Python2 script and feed it to a | Python3 interpreter, no, in general that will not work. | They simply aren't the same language. Even a simple | "print x" will do you in. | pdonis wrote: | _> The same .py file runs under python2 and python3?_ | | Sure, as long as it doesn't contain any syntax or | spellings which are incompatible between the two. That's | a fairly large subset of the language. | | _> if you bring a Python2 script and feed it to a | Python3 interpreter, no, in general that will not work. | They simply aren 't the same language. Even a simple | "print x" will do you in._ | | But this will work: from __future__ | import print_function print(x) | | This is valid under both Python 2 and Python 3. | | Also, as I said above, there is a pretty large subset of | the Python language that has the same syntax and | spellings in both Python 2 and Python 3, and any script | or module or package that only uses that subset will run | just fine under both interpreters. You are drastically | underestimating both the size and the usage of this | subset of the language. | 4ec0755f5522 wrote: | A lot of projects write in that style e.g. compatible | with both python2 and python3, it's really common because | there's so much py2 deployed (was default on centOS until | very recently, still default on osx, etc.) | | Nearly every py3 feature was backported to 2 you just | need to write it in a compatible way. I'm seeing some | drop py2 support now though. Which I'm fine with, I | haven't written python2 code in maybe 6 or 7 years now. | Scarblac wrote: | Say Django 1.11, a massive amount of .py files, works | completely fine under both 2 and 3. As do many other | libraries. | | Yes you often need some precautions like "from __future__ | import" statements and sometimes libraries like `six`, | but it's been perfectly normal practice for most of the | last decade. | BiteCode_dev wrote: | No, but it is possible to write Python 2 code than runs | in Python 3. | | In fact, the vast majority of popular libs had a 2/3 | compatible code base for a few years. | | The hard part was not the syntax in fact. It's pretty | trivial: the language are not that different. | | The hard part is the I/O stack, because the stdlib is | very different, espacially for this part. | cesarb wrote: | > Or, alternatively, "How many Python 2 scripts will run | on a Python 3 interpreter?" Answer: "None of them." | | That's obviously untrue. For example, consider the | following Python 2 script: with | open('a.txt', 'r') as a, open('b.txt', 'w') as b: | for line in a: b.write(line) | | It works identically on a Python 3 interpreter, and it | doesn't even use "from __future__ import ...". | downerending wrote: | Yes, I should have said, "a vanishingly small proportion, | and even then mostly by accident". | | My guess is that if you sweep GitHub for Python2 code and | push it into Python3, that proportion would be under one | percent. | mixmastamyk wrote: | As a big user of logging and little to do with character | coding, all of my admin/daemon stuff moved over with | almost no changes necessary for 3.0 (actually ~3.3). | | For some projects I did bigger refactors for 2.6/7 | (exceptions) and 3.6 (fstrings). | dtech wrote: | > Or, alternatively, "How many Python 2 scripts will run | on a Python 3 interpreter?" Answer: "None of them." | | That is not true though, a lot of 2 scripts will run no | problem, especially if import future is used | chc wrote: | Ruby did something like this around the same time Python | did. Ruby's was a bit smaller, but overall a roughly | similar amount of breaking changes. They forced you to | think about encodings more with Strings, they changed the | signatures of several operators, they changed some of the | syntax for case statements, they drastically changed the | scope rules for block variables, they restructured the | base object hierarchy, etc. In both cases, it was a | deliberate decision to make a clean break. I think Ruby's | big break didn't make as big a schism mainly because | Rails was very supportive, and Rails holds an enormous | amount of influence in the Ruby world. | | If Python 3 had been introduced as a separate language, | I'm pretty sure everyone would have said "Why isn't this | just called Python 3? It's 99.9% the same as Python and | it's by the same people and they're deprecating Python in | favor of it." | BiteCode_dev wrote: | First, there are not a lot of interpretted (not compiled, | that's another matter entirely) languages that are as old | as Python. | | And there are really few that are even near Python | popularity, or used with such diversity as Python. | | I mean, you can get away with keeping AWK the way it was | 2 decades ago, nobody is going to use it for for machine | learning or to teach computing in all universities in the | world on 3 operating systems, utilizing C extensions, or | processing Web API. | | Among the few that would even compare, there are the ones | that have accumulated so much cruft that they became | unusable from today's standard (E.G: bash). Then you have | those who have done like Python (E.G: perl 6). The ones | that just tried and failed (PHP 6). The ones that broke | compat and told everybody move or die (Ruby in a point | release, gave basically 2 years). And the ones that | created a huge pile of horror they called full stack to | keep going (E.G: JS). Also those that got hijacked by | vendors and just exploded in miriads of proprietary | syntaxes (E.G: SQL) or completely new concepts (E.G: | lisp). | | At least, in Python you CAN write Python 2/3 compatible | code, and you have a LOT of tooling to help you with | that, or migrating. | | So, yes, the Python 2 -> 3 transition could have been | better. Insight is 20/20. | | But I'm struggling to think of any other language in a | similar situation that has done better. | delaaxe wrote: | Hindsight 20/20, not insight. | BiteCode_dev wrote: | Thanks. French here. Can't edit anymore though | jnwatson wrote: | Swift has had 3 backwards incompatible versions of larger | scope in less time. | tartrate wrote: | re.sub? ___________________________________________________________________ (page generated 2020-05-28 23:00 UTC)