Seite 1 von 1

Python 3.9 ist da!

Verfasst: Dienstag 6. Oktober 2020, 03:43
von snafu
Ein paar neue Features:
  • Zugriff auf Zeitzonen durch das neue zoneinfo-Modul
  • Leichteres Zusammenführen von Wörterbüchern durch den Union-Operator (|)
  • Zusatzinfos bei Type-Hints nun möglich durch Annotated
  • Umstellung auf einen PEG-Parser (bisher wurde LL(1) genutzt) für weitere moderne Konstrukte in der Python-Syntax
  • Einführung von removeprefix() und removesuffix() für Strings
  • Nutzung von Builtin-Typen für Annotations (typing.List[int] -> list[int])
und vieles mehr

Eine ausführliche englische Beschreibung (dessen Übersicht ich als Vorlage genommen habe) findet sich hier:
https://realpython.com/python39-new-features/

Re: Python 3.9 ist da!

Verfasst: Dienstag 6. Oktober 2020, 03:49
von snafu
Eigentlich ist das unpassend im Offtopic-Forum. Könnte das jemand bitte nach "Links und Tutorials" verschieben...? :)

Re: Python 3.9 ist da!

Verfasst: Dienstag 6. Oktober 2020, 12:51
von DeaD_EyE
Der neue Parser ermöglicht nun auch Syntax, der aufgrund des LL(1) parsers nicht möglich gewesen ist.

Code: Alles auswählen

from pathlib import Path

desktop = Path.home() / "Desktop"
file1 = desktop / "test_file_1.txt"
file2 = desktop / "test_file_2.txt"


with (
    file1.open("w") as fd1,
    file2.open("w") as fd2,
):
    fd1.write("Hello World 1\n")
    fd2.write("Hello World 2\n")
PS C:\Users\Admin\Desktop> py -3.8 .\peg_py.py
File ".\peg_py.py", line 9
file1.open("w") as fd1,
^
SyntaxError: invalid syntax
PS C:\Users\Admin\Desktop> py -3.9 .\peg_py.py
PS C:\Users\Admin\Desktop>

Re: Python 3.9 ist da!

Verfasst: Dienstag 6. Oktober 2020, 16:19
von nezzcarth
Ich verstehe das Beispiel nicht so ganz. So geht es doch:

Code: Alles auswählen

In [20]: with f1.open('w') as file1, \
    ...:      f2.open('w') as file2:
    ...:     file1.write('a')
    ...:     file2.write('b')
Wäre das mit der Klammer wirklich "schöner"? Warum würde man diese Syntax haben wollen?

Re: Python 3.9 ist da!

Verfasst: Dienstag 6. Oktober 2020, 16:53
von __blackjack__
Weil man \ nicht haben will. Da darf zum Beispiel kein „whitespace“ hinter stehen. An allen anderen Stellen kann man Klammern verwenden damit der Compiler weis, das der Ausdruck am Zeilenende noch nicht zu ende sein kann.

Edit: PEP 8 zu dem Thema:
The preferred way of wrapping long lines is by using Python’s implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation.
Als Beispiel für \ ist dann da auch ``with`` genannt, weil's nicht anders geht/ging.

Re: Python 3.9 ist da!

Verfasst: Dienstag 6. Oktober 2020, 17:14
von snafu
Das mit dem with-Mehrzeiler ist wohl auch eher als netter Nebeneffekt zu sehen. Als eine der nächsten großen Erweiterungen in der Python-Syntax ist jedenfalls das Pattern-Matching geplant mittels match-case Konstrukten. Dies war mit dem alten Parser nicht umsetzbar. Dementsprechend lautet auch die Aussage in PEP 622:
The match statement uses a soft keyword, and it is one of the first major Python features to take advantage of the capabilities of the new PEG parser.
Quelle: https://www.python.org/dev/peps/pep-062 ... arty-tools

Cython, PyPy und Konsorten werden jedenfalls ihren "Spaß" haben, wenn diese Neuerung kommt...

Re: Python 3.9 ist da!

Verfasst: Dienstag 6. Oktober 2020, 17:27
von nezzcarth
@__blackjack__:
Okay. Danke für die Erklärung. Persönlich stört mich das \ nicht besonders, aber ist es gut, wenn die Syntax einheitlicher wird.

Re: Python 3.9 ist da!

Verfasst: Dienstag 6. Oktober 2020, 17:38
von snafu
Übrigens, im selben PEP wird auch erklärt, was der wesentliche Unterschied durch die Nutzung als Soft Keyword ist:
The difference between hard and soft keywords is that hard keywords are always reserved words, even in positions where they make no sense (e.g. x = class + 1), while soft keywords only get a special meaning in context. Since PEP 617 the parser backtracks, that means that on different attempts to parse a code fragment it could interpret a soft keyword differently.

For example, suppose the parser encounters the following input:

Code: Alles auswählen

match [x, y]:
The parser first attempts to parse this as an expression statement. It interprets match as a NAME token, and then considers [x, y] to be a double subscript. It then encounters the colon and has to backtrack, since an expression statement cannot be followed by a colon. The parser then backtracks to the start of the line and finds that match is a soft keyword allowed in this position. It then considers [x, y] to be a list expression. The colon then is just what the parser expected, and the parse succeeds.
https://www.python.org/dev/peps/pep-062 ... patibility

Re: Python 3.9 ist da!

Verfasst: Dienstag 6. Oktober 2020, 18:20
von __blackjack__
PyPy ist ja gerade erst bei Python 3.7 als alpha-Version. Die haben noch Zeit. :-)

Interessanter wird das mit Softkeywords beim Syntaxhighlighting. Die Ansätze dort in den verschiedenen Editoren & Co sind wahrscheinlich nicht alle flexibel genug für so etwas.

Re: Python 3.9 ist da!

Verfasst: Donnerstag 8. Oktober 2020, 13:30
von DeaD_EyE
Habt ihr schon den TopologicalSorter ausprobiert?

Mit Graphen hab ich eigentlich nicht viel am Hut, habe dann aber ein Beispiel gesucht, wofür z.B. der TopologicalSorter nutzbar ist.
Ich hatte dann die Idee, dass man damit z.B. Abhängigkeiten auflösen kann, was ja apt-get und die anderen Paketmanager auch machen.

Code: Alles auswählen

import sys
from collections import defaultdict
from itertools import chain
from pprint import pprint
from subprocess import DEVNULL, CalledProcessError, check_output
from graphlib import TopologicalSorter


def get_deps(package):
    packages = []
    cmd = ("apt-cache", "depends", package)
    stdout = check_output(cmd, encoding="utf8", stderr=DEVNULL)
    try:
        for line in stdout.splitlines():
            if "Depends:" in line:
                _, p = line.split(":", maxsplit=1)
                if "<" in p:
                    continue
                packages.append(p.strip())
    except CalledProcessError:
        return []
    else:
        return packages


def main(p):
    deps = defaultdict(set)
    deps[p] = set(get_deps(p))
    while missing := (set(chain.from_iterable(deps.values())) - deps.keys()):
        for dep in missing:
            if dep not in deps:
                deps[dep] = set(get_deps(dep))
    # remove cyclic redundancies
    for name, p_deps in deps.items():
        for sub in p_deps:
            if sub in deps and name in deps[sub]:
                print("Removing:", name)
                deps[sub].remove(name)
    return dict(deps)


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print(sys.executable, sys.argv[0], "package-name")
        sys.exit(1)
    package = sys.argv[1].strip().lower()
    deps = main(package)
    tps = TopologicalSorter(deps)
    tps.prepare()
    while tps.is_active():
        to_install = tps.get_ready()
        print(to_install)
        for install in to_install:
            tps.done(install)

andre@DESKTOP-F29NT09:/mnt/c/Users/Admin$ python dependencies2.py python3
Removing: libc6
Removing: libc6
('mime-support', 'libcrypt1', 'gcc-10-base')
('libgcc-s1',)
('libc6',)
('libdb5.3', 'libbz2-1.0', 'liblzma5', 'libexpat1', 'libsqlite3-0', 'libzstd1', 'libtinfo6', 'libffi7', 'libmpdec2', 'libuuid1', 'zlib1g', 'libacl1', 'libpcre2-8-0')
('libncursesw6', 'libselinux1')
('tar',)
('dpkg',)
('install-info', 'perl-base')
('readline-common', 'debconf')
('libreadline8', 'libssl1.1')
('libpython3.8-minimal',)
('python3.8-minimal', 'libpython3.8-stdlib')
('python3-minimal', 'python3.8', 'libpython3-stdlib')
('python3',)