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.