Мини код ревью Python кода

37. Мини код ревью Python кода

Условие задачи: Расскажи как работает код ниже

a = 10
b = a

del(a)

c = b

print(c)
class Test():
    a = 123

test = Test()
test2 = Test()

Test.a = 345

print(Test.a)
print(test2.a)
# В чем проблема этого кода?
from fastapi import FastAPI

app = FastAPI()


@app.get("/item")
async def read_user():
    return ["Rick", "Morty"]


@app.get("/item")
async def read_user2():
    return ["Bean", "Elfo"]
Спойлеры к решению
Подсказки
  • del(a) удаляет имя a, а не сам объект, если на объект ещё есть другие ссылки.
  • После b = a переменные a и b ссылаются на один и тот же объект 10.
  • Атрибут класса доступен через сам класс и через экземпляры класса.
  • Если у экземпляра нет собственного атрибута a, Python берёт a из класса.
  • В FastAPI нельзя нормально объявлять два обработчика с одинаковым HTTP-методом и одинаковым путём.
  • При одинаковом GET /item один endpoint фактически перекрывает другой с точки зрения маршрутизации/документации.
Решение

Первый фрагмент:

a = 10
b = a

del(a)

c = b

print(c)

Результат:

10

Разбор:

a = 10
b = a

После этого a и b указывают на один и тот же объект 10.

Дальше:

del(a)

удаляет только имя a. Сам объект 10 не исчезает, потому что на него всё ещё ссылается переменная b.

Потом:

c = b

переменная c тоже начинает ссылаться на значение 10, поэтому:

print(c)

выведет:

10

Второй фрагмент:

class Test():
    a = 123

test = Test()
test2 = Test()

Test.a = 345

print(Test.a)
print(test2.a)

Результат:

345
345

Разбор:

class Test():
    a = 123

a — это атрибут класса, а не атрибут конкретного объекта.

Когда создаются объекты:

test = Test()
test2 = Test()

у них нет собственного атрибута a. Поэтому при обращении:

test2.a

Python ищет a сначала у объекта test2, не находит, затем берёт a из класса Test.

После строки:

Test.a = 345

атрибут класса меняется с 123 на 345.

Поэтому:

print(Test.a)
print(test2.a)

выведет:

345
345

Третий фрагмент:

from fastapi import FastAPI

app = FastAPI()


@app.get("/item")
async def read_user():
    return ["Rick", "Morty"]


@app.get("/item")
async def read_user2():
    return ["Bean", "Elfo"]

Проблема в том, что оба обработчика зарегистрированы на один и тот же маршрут:

GET /item
GET /item

Это плохая и неоднозначная реализация. Для одного HTTP-метода и одного URL должен быть один обработчик.

На практике первый зарегистрированный обработчик может обработать запрос:

@app.get("/item")
async def read_user():
    return ["Rick", "Morty"]

То есть при запросе GET /item можно получить:

["Rick", "Morty"]

А второй обработчик:

@app.get("/item")
async def read_user2():
    return ["Bean", "Elfo"]

окажется недостижимым для этого же маршрута.

Исправить можно так — сделать разные пути:

from fastapi import FastAPI

app = FastAPI()


@app.get("/rick-and-morty")
async def read_user():
    return ["Rick", "Morty"]


@app.get("/disenchantment")
async def read_user2():
    return ["Bean", "Elfo"]

Или оставить один путь, но сделать один обработчик:

from fastapi import FastAPI

app = FastAPI()


@app.get("/item")
async def read_items():
    return {
        "rick_and_morty": ["Rick", "Morty"],
        "disenchantment": ["Bean", "Elfo"],
    }

Итог:

1. Первый код выведет 10.
2. Второй код выведет 345 и 345.
3. В FastAPI-коде проблема в дублировании маршрута GET /item.