【问题标题】:Python "requests" with decorator带有装饰器的 Python“请求”
【发布时间】:2020-02-19 20:41:12
【问题描述】:

我正在尝试执行我的代码:

import json
import requests

class Request(object):
  def __init__(self, method, path):
    self.method = method
    ## Q1
    self.url = ''.join([***MyClass.host***, path])
    self.h = {
      "Content-Type": "application/json;charset=UTF-8"
    }

  def __call__(self, f):
    def wrapper(*args, **kwargs):
      # Q2
      getattr(requests, self.method)(url = self.url, headers = h***, data = json.dumps(body)***)
    return wrapper

class MyClass(object):
  def __init__(self, host):
    self.host = host

  @Request(method = "post", path = "/add_mission")
  def AddMission(self, mission):
    body = {
      "mission_id": mission
    }

  @Request(method = "get", path = "/system_info")
  def GetInfo(self):
    print("I want get info")

有一些问题希望有人能解决我的问题:
1. 我的装饰器“Request”如何从“MyClass”中获取变量“host”? (在评论 ## Q1)
2.如何将函数的变量“body”传递给装饰器,可以吗? (在评论 ## Q2)

因为我需要使用 [post, get, delete] 访问不同的 url(host+path)。 我不确定这种情况下的装饰器是否匹配? 或者也许有更好的方法来处理这种情况?

【问题讨论】:

  • 你不需要getattr(requests, self.method)requests.get 已经只是 requests.request("GET", ...) 的包装。
  • @chepner 谢谢你的提醒。你的回复消除了我的困惑。 :)

标签: python python-requests decorator python-decorators


【解决方案1】:

通过调用对象的host 属性调用修饰函数后,主机就可用,因此请等到您实际调用该方法来构建URL。您需要调用底层函数来取回一个值(如果有的话)用作有效负载。

import json
import requests

class Request(object):
    def __init__(self, method, path):
        self.method = method.upper()
        self.h = {
            "Content-Type": "application/json;charset=UTF-8"
        }
        self.path = path

  def __call__(self, f):
      def wrapper(obj, *args, **kwargs):
          payload = f(*args, **kwargs)
          args = {
              'method': self.method,
              'url': 'http://{}/{}'.format(obj.host, self.path),
              'headers': self.h
          }
          if payload is not None:
              args['json'] = payload
          return requests.request(**args)
      return wrapper


class MyClass(object):
    def __init__(self, host):
        self.host = host

    @Request(method="post", path="/add_mission")
    def AddMission(self, mission):
        return {"mission_id": mission}

    @Request(method="get", path="/system_info")
    def GetInfo(self):
        print("I want get info")

然后你可以写,例如,

api = MyClass("www.example.com")
add_response = api.AddMission("mars")
info_response = api.GetInfo()

每个修饰函数返回调用requests.request产生的Response对象。

您可以为每个方法考虑 Request 的子类:

class GetRequest(Request):
    def __init__(self, path):
        super().__init__("GET", path)

class PostRequest(Request):
    def __init__(self, path):
        super().__init__("POST", path)

# etc

【讨论】:

  • 非常感谢!我将payload = f() 更改为payload = f(obj, *args, **kwargs),它按我的预期工作。我想我需要更多地了解装饰器和包装器:)
【解决方案2】:

代码现在涵盖了所有类型的请求,以及对每个请求进行身份验证的可能性。

Code on github

优点:

  1. 易于阅读,代码更少,因此更易于维护。
  2. 轻松添加新方法或更新现有方法。

缺点:

  1. 无法让它在 pytest 上运行。
  2. 较少的“标准”API 可能会导致难以调试的错误。
  3. 不太“标准”的 API 可能需要更改可能很棘手的包装器。
  4. 由于所有嵌套函数调用(方法/装饰器是函数)导致性能损失很大

import ujson
import requests

class Request(object):
    def __init__(self, method, path, h={ "Content-Type": "application/json;charset=UTF-8" }):
        self.path = path
        #print(f"PATH {path}")
        self.method = method.upper()
        self.h = h

    def __call__(self, f):
        def wrapper(obj, *args, **kwargs):
            payload = f(obj, *args, **kwargs)
            #print(f"PAYLOAD {payload}")
            if (payload is not None) and (self.method == "GET" or self.method == "DELETE"):
                dic_key = list(payload.keys())[0]
                self.path = "{}/?{}={}".format(self.path, dic_key, payload[dic_key])
                payload = None
            args = {
                'method': self.method,
                'url': 'http://{}/{}'.format(obj.host, self.path),
                'headers': self.h
            }
            if payload is not None:
                args['json'] = payload
            return requests.request(**args)
        return wrapper

class GetRequest(Request):
    def __init__(self, path, h=None):
        if h is None:
            super().__init__("GET", path)
        else:
            super().__init__("GET", path, h)

class PostRequest(Request):
    def __init__(self, path, h=None):
        if h is None:
            super().__init__("POST", path)
        else:
            super().__init__("POST", path, h)

class PutRequest(Request):
    def __init__(self, path, h=None):
        if h is None:
            super().__init__("PUT", path)
        else:
            super().__init__("PUT", path, h)

class DeleteRequest(Request):
    def __init__(self, path, h=None):
        if h is None:
            super().__init__("DELETE", path)
        else:
            super().__init__("DELETE", path, h)

myToken = 'Secret_key_1234567890'

class DecoRequests(object):
    def __init__(self, host):
        self.host = host

    @GetRequest(path="userlist", h={'access_token':myToken})
    def list_employees(self):
        pass

    @GetRequest(path="users", h={'access_token':myToken})
    def list_employee(self, username):
        return {"username":username}

    @PostRequest(path="users", h={'access_token':myToken})
    def add_employee(self, employee_data):
        return employee_data

    @PutRequest(path="users", h={'access_token':myToken})
    def update_employee(self, employee_data):
        return employee_data

    @DeleteRequest(path="users", h={'access_token':myToken})
    def rm_employee(self, username):
        return {"username":username}

【讨论】:

    猜你喜欢
    • 2014-02-16
    • 1970-01-01
    • 2014-07-21
    • 2011-05-28
    • 1970-01-01
    • 2014-10-04
    • 2018-08-20
    • 2018-11-21
    • 1970-01-01
    相关资源
    最近更新 更多