๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ Python

Python 3.11 ๋‹ฌ๋ผ์ง„ ์  - ์—…๋ฐ์ดํŠธ

by dev.py 2024. 1. 15.

์†๋„

๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด 3.10์— ๋น„ํ•ด 10% ~ 60% ๋นจ๋ผ์กŒ๋‹ค. ์ฆ‰ 1.25 ๋ฐฐ ๋นจ๋ผ์กŒ๋‹ค

 

 

์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ƒˆ ๊ตฌ๋ฌธ (except*) 

์˜ˆ์™ธ๋ฌธ์— ๋Œ€ํ•œ ๊ทธ๋ฃน์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ณ , ํ•œ๋ฒˆ์— ์—ฌ๋Ÿฌ ์˜ˆ์™ธ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.๋˜ํ•œ ์ถœ๋ ฅ๋ฌธ์„ ์˜ˆ์˜๊ฒŒ ๊ทธ๋ ค์ค€๋‹ค

def f():
    raise ExceptionGroup(
        "group1",
        [
            OSError(1),
            SystemError(2),
            ExceptionGroup(
                "group2",
                [
                    OSError(3),
                    RecursionError(4)
                ]
            )
        ]
    )

try:
    f()
except* OSError as e:
    print("There were OSErrors")
except* SystemError as e:
    print("There were SystemErrors")

There were OSErrors
There were SystemErrors
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 2, in <module>
  |   File "<stdin>", line 2, in f
  | ExceptionGroup: group1
  +-+---------------- 1 ----------------
    | ExceptionGroup: group2
    +-+---------------- 1 ----------------
      | RecursionError: 4
      +------------------------------------

# tomlib ์ถ”๊ฐ€

๋ฐ์ดํ„ฐ ์ง๋ ฌํ™” ํฌ๋งท์ธ TOML์„ ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋‚ด์žฅ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. 

import tomllib

with open("pyproject.toml", "rb") as f:
    data = tomllib.load(f)

์œ ์‚ฌ ๋ฐ์ดํ„ฐ  JSON, YAML๊ณผ ๋น„๊ตํ•˜๋ฉด

ํŠน์ง• JSON YAML TOML
ํฌ๋งท ์ข…๋ฅ˜ ๋ฐ์ดํ„ฐ ๊ตํ™˜ ์ธ๊ฐ„์ด ์ฝ๊ธฐ ์‰ฌ์šด ๋ฐ์ดํ„ฐ ์„ค์ • ํŒŒ์ผ์šฉ
๋ฌธ๋ฒ• ์—„๊ฒฉํ•˜๊ณ  ๊ฐ„๊ฒฐํ•œ ์‚ฌ์šฉ์ž ์นœํ™”์ ์ด๊ณ  ์œ ์—ฐํ•œ ํ‚ค-๊ฐ’ ์Œ
์ฃผ์„ ์ฒ˜๋ฆฌ ์ง€์›ํ•˜์ง€ ์•Š์Œ ์ง€์› ์ง€์›
๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ธฐ๋ณธ์ ์ธ ํƒ€์ž… (๋ฌธ์ž์—ด, ์ˆซ์ž, ๊ฐ์ฒด, ๋ฐฐ์—ด, ๋ถˆ๋ฆฌ์–ธ, ๋„) ํ’๋ถ€ํ•œ ํƒ€์ž… (JSON ํƒ€์ž… ์ง€์› ๋ฐ ์ด์ง„ ๋ฐ์ดํ„ฐ ๋“ฑ) ํ’๋ถ€ํ•œ ํƒ€์ž… (๊ธฐ๋ณธ ํƒ€์ž… ๋ฐ ๋‚ ์งœ ์ง€์›)
๊ฐ€๋…์„ฑ ์ค‘๊ด„ํ˜ธ์™€ ๋”ฐ์˜ดํ‘œ ๋•Œ๋ฌธ์— ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง ์ตœ์†Œํ•œ์˜ ๊ตฌ๋‘์ ์œผ๋กœ ๋งค์šฐ ๊ฐ€๋…์„ฑ์ด ์ข‹์Œ ๋ช…ํ™•ํ•œ ๊ตฌ์กฐ๋กœ ๊ฐ€๋…์„ฑ์ด ์ข‹์Œ
์ž‘์„ฑ ์šฉ์ด์„ฑ ์—„๊ฒฉํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ์ธํ•ด ์ž‘์„ฑ์ด ๋œ ํŽธ๋ฆฌํ•จ ๊ฐ„๊ฒฐํ•˜๊ณ  ์‰ฌ์›€ ์ง๊ด€์ ์ด๊ณ  ์‰ฌ์›€
ํŒŒ์ผ ํ™•์žฅ์ž .json .yaml ๋˜๋Š” .yml .toml
์‚ฌ์šฉ์ฒ˜ ์›น API, ํ˜„๋Œ€ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ค์ • ์„ค์ • ํŒŒ์ผ, ๋ฐ์ดํ„ฐ ์ง๋ ฌํ™” ์„ค์ • ํŒŒ์ผ, Rust ํ™˜๊ฒฝ์—์„œ ๋” ์ผ๋ฐ˜์ 

 

์—๋Ÿฌ ๋ฌธ๊ตฌ ๊ฐœ์„ 

์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๋ถ€๋ถ„์„ ๋ฐ‘์ค„๋กœ ์•Œ๋ ค์ค€๋‹ค. 

์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ ๊ฐ€์žฅ ํฌ๊ฒŒ ๋‹ค๊ฐ€์™”๋‹ค.

x ๋ณ€์ˆ˜์— ๋Œ€ํ•œ NoneType์—์„œ ์–ด๋–ค ๋ณ€์ˆ˜์˜ x๋ฅผ ์กฐํšŒํ•˜๋‹ค๊ฐ€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€, ๋ฐ‘์ค„๋กœ ์•Œ๋ ค์ค€๋‹ค.

Traceback (most recent call last):
  File "distance.py", line 11, in <module>
    print(manhattan_distance(p1, p2))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "distance.py", line 6, in manhattan_distance
    return abs(point_1.x - point_2.x) + abs(point_1.y - point_2.y)
                           ^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'x'

์•„์‰ฌ์šด ๊ฑด ์•„๋ž˜ ์ฝ”๋“œ์—์„œ

์ œ๋กœ ๋””๋ฐ”์ด๋“œ์— ๋Œ€ํ•ด ์—ฐ์‚ฐ์ž ๊ธฐ์ค€์œผ๋กœ ์•Œ๋ ค์ค€๋‹ค. (๋ณ€์ˆ˜ ๊ธฐ์ค€์ด์—ˆ๋‹ค๋ฉด)

raceback (most recent call last):
  File "calculation.py", line 54, in <module>
    result = (x / y / z) * (a / b / c)
              ~~~~~~^~~
ZeroDivisionError: division by zero

 

Variadic ์ œ๋„ˆ๋ฆฌ

TypeDict๋ฅผ ์ƒ์†๋ฐ›์œผ๋ฉดJava - Spring ์—์„œ์˜ ๋ชจ๋ธ์ด๋‚˜ objectMapper.readValue ์ฒ˜๋Ÿผ, ํด๋ž˜์Šค-๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ์— ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค.

class Movie(TypedDict):
   title: str
   year: NotRequired[int]

m1: Movie = {"title": "Black Panther", "year": 2018}  # OK
m2: Movie = {"title": "Star Wars"}  # OK (year is not required)
m3: Movie = {"year": 2022}  # ERROR (missing required field title)

NotRequired ์„ค์ •๋„ ๊ฐ€๋Šฅํ•˜๋‹ค

class Movie(TypedDict, total=False):
   title: Required[str]
   year: int

 

Self Return ์‚ฌ์šฉ

๊ธฐ์กด์—๋Š” self๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ํƒ€์ž… ํžŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

class Shape:
    def set_scale(self, scale: float) -> Shape:
        self.scale = scale
        return self

Shape().set_scale(0.5)  # => Shape

ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๊ฒฝ์šฐ, Circle().set_scale(0.5) ์—์„œ Shape ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜๋˜์–ด, set_radius()๋ฅผ ํ˜ธ์ถœ ํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค.

class Circle(Shape):
    def set_radius(self, r: float) -> Circle:
        self.radius = r
        return self

Circle().set_scale(0.5)  # *Shape*, not Circle
Circle().set_scale(0.5).set_radius(2.7)
# => Error: Shape has no attribute set_radius

๊ธฐ์กด ๋ฒ„์ „์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ„๋„์˜ TShap๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ–ˆ์ง€๋งŒ

from typing import TypeVar

TShape = TypeVar("TShape", bound="Shape")

class Shape:
    def set_scale(self: TShape, scale: float) -> TShape:
        self.scale = scale
        return self


class Circle(Shape):
    def set_radius(self, radius: float) -> Circle:
        self.radius = radius
        return self

Circle().set_scale(0.5).set_radius(2.7)  # => Circle

Self ๋ผ๋Š” ์ง๊ด€์ ์ธ ํ˜•ํƒœ๋ฅผ ์ œ๊ณตํ•˜์—ฌ ๊ฐœ์„ ํ•˜์˜€๋‹ค.

from typing import Self

class Shape:
    def set_scale(self, scale: float) -> Self:
        self.scale = scale
        return self


class Circle(Shape):
    def set_radius(self, radius: float) -> Self:
        self.radius = radius
        return self

 

์‚ฌ์šฉํ•œ ์ฝ”๋“œ๋Š” ๋ชจ๋‘ PEP ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ–ˆ์Šต๋‹ˆ๋‹ค.