当前位置: 首页 > 知识库问答 >
问题:

快速API-将函数类型设置为与导入方法相同的类型?

袁运良
2023-03-14

我正在构建一个快速API服务器,代表我的客户提供代码。

所以我的目录结构是:

project
|  main.py
|--customer_code (mounted at runtime)
   |  blah.py

main.py中我有:

from customer_code import blah
from fastapi import FastAPI

app = FastAPI()

...

@app.post("/do_something")
def bar(# I want this to have the same type as blah.foo()):
    blah.foo()

blah.py中我有:

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name = 'John Doe'

def foo(data : User):
    # does something

我不知道我的客户的代码(在blah.py中)会是什么类型。但我想使用FastAPI内置的OpenAPI模式生成(而不是要求我的客户接受和解析JSON输入)。

有没有办法将要的参数类型设置为与要 foo 的参数类型相同?

似乎有一种方法是用花哨的字符串插值来执行exec,但我担心这不是真正的Pythonic,我还必须清理用户的输入。所以如果有另一个选择,很想学习。

共有3个答案

鞠嘉志
2023-03-14

首先考虑装饰者的定义。来自Python Decorators入门:

装饰器是一个函数,它接受另一个函数并扩展后一个函数的行为而不显式修改它

app.post(“/do_something”) 返回一个装饰器,该装饰器接收示例中的 def bar(...) 函数。关于@的用法,前面提到的同一页面说:

@my_decorator只是更简单的表示方式 say_whee = my_decorator(say_whee)

所以你可以用这样的话:

app.post("/do_something")(blah.foo)

blahfoo函数将由FastAPI公开。除非您还想在调用foo之前或在返回foo<-code>之后执行一些操作,否则它可能会结束。如果是这种情况,你需要有自己的装饰师。

完整示例:

# main.py
import functools
import importlib
from typing import Any

from fastapi import FastAPI

app = FastAPI()


# Create your own decorator if you need to intercept the request/response
def decorator(func):

    # Use functools.wraps() so that the returned function "look like"
    # the wrapped function
    @functools.wraps(func)
    def wrapper_decorator(*args: Any, **kwargs: Any) -> Any:
        # Do something before if needed
        print("Before")
        value = func(*args, **kwargs)
        # Do something after if needed
        print("After")
        return value

    return wrapper_decorator


# Import your customer's code
blah = importlib.import_module("customer_code.blah")

# Decorate it with your decorator and then pass it to FastAPI
app.post("/do_something")(decorator(blah.foo))
# customer_code.blah.py

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name = 'John Doe'


def foo(data: User) -> User:
    return data

万明辉
2023-03-14

最后使用了以下答案:

from fastapi import Depends, FastAPI
from inspect import signature
from pydantic import BaseModel, create_model


sig = signature(blah.foo)

query_params = {}
for k in sig.parameters:
  query_params[k] = (sig.parameters[k].annotation, ...)

query_model = create_model("Query", **query_params)

所以这个函数看起来像这样:

@app.post("/do_something")
def bar(params: query_model = Depends()):
    p_as_dict = params.as_dict()
    return blah.foo(**p_as_dict)
赏成益
2023-03-14

这回答了你的问题吗?

def f1(a: str, b: int):
  print('F1:', a, b)

def f2(c: float):
  print('F2:', c)

def function_with_unknown_arguments_for_f1(*args, **kwargs):
  f1(*args, **kwargs)

def function_with_unknown_arguments_for_f2(*args, **kwargs):
  f2(*args, **kwargs)

def function_with_unknown_arguments_for_any_function(func_to_run, *args, **kwargs):
  func_to_run(*args, **kwargs)

function_with_unknown_arguments_for_f1("a", 1)
function_with_unknown_arguments_for_f2(1.1)
function_with_unknown_arguments_for_any_function(f1, "b", 2)
function_with_unknown_arguments_for_any_function(f2, 2.2)

输出:

F1: a 1
F2: 1.1
F1: b 2
F2: 2.2

这是关于参数夸格的详细说明

换句话说,函数应该像

@app.post("/do_something")
def bar(*args, **kwargs):
    blah.foo(*args, **kwargs)

为了能够处理动态变化的foo

关于OpenAPI

非常确定可以覆盖文档生成器类或函数,并根据foo而不是特定视图的bar设置有效负载类型。
以下是如何扩展OpenAPI的几个示例。它与您的问题没有直接关系,但可能有助于理解它是如何工作的。

 类似资料:
  • 我想这样设置参数的类型: 当我尝试这样做时,会引发错误:NameError:名称'Tree'未定义

  • 我的问题与新Python的类型提示有关。我试图在对象的方法中添加一个类型提示,它具有与对象相同类型的参数,但PyCharm将我标记为错误()。问题如下: 因此,问题是如何正确定义参数的类型。也许是正确的?

  • 函数 要声明一个函数,需要使用关键字fn,后面跟上函数名,比如 fn add_one(x: i32) -> i32 { x + 1 } 其中函数参数的类型不能省略,可以有多个参数,但是最多只能返回一个值, 提前返回使用return关键字。Rust编译器会对未使用的函数提出警告, 可以使用属性#[allow(dead_code)]禁用无效代码检查。 Rust有一个特殊特性适用于发散函数 (d

  • 我想使返回数据类型的的函数与传入的参数的数据类型相同。例如,我会这样调用函数: 因为我将一个传递给,所以它返回了一个

  • 问题内容: 我已经看到了一些类似的问题两种不同的类型如何使用接口在golang中实现相同的方法?,但就我而言,我的类型没有相同的基本类型。我的类型是不同大小的数组。 因此,可能不重复两种方法GetByte0()? 问题答案: 例如, 输出:

  • 我在Java中有两个几乎相同的方法。唯一的区别是它们有不同的参数类型。它们使用泛型并返回输入参数的类型T。我怎样才能摆脱重复的代码?下面是我的两个方法。最后,它们都使用不同的类型调用Spring。否则,方法是相同的。