【问题标题】:Call arbitrary methods on class like xmlrpclib.ServerProxy在类上调用任意方法,如 xmlrpclib.ServerProxy
【发布时间】:2017-07-25 23:32:11
【问题描述】:

我有一个远程 api,只接受可能被称为类似的 XML

api = xmlrpclib.ServerProxy(IP)
result = api.Execute({"Method": 'Method_name', "Params": [P1, P2],
                      ...  # technical values - authentication, etc
                      })

技术值不会经常更改 - 整个脚本通常会为它们使用相同的值,因此我创建了一个具有方法的类

def call(self, method_name, params):
    self.api.Execute(<constructed dictionary>)

我想知道是否可以将这些方法直接调用为self.api 的方法,例如:

self.api.method_name(params)

这将依次填写.Execute 和我的其余部分,总体思路如下:

def __getattr__(self, item):
    if item in self.__dict__:
        return self.item
    else:
        return functools.partial(self.call, method_name=item)

所以,如果我在类中定义了一个方法(self.fooself.bar 等)——调用它会产生self.fooself.bar 的真实结果。

self.api.METHOD(params) 这样调用它,其中METHOD 是一个自定义的方法,但是这种方法“污染”了我没有定义的其他方法,比如self.api.__repr__ 等等

根据this question,我覆盖__getitem__ 的方法是否完全不正确?如果是这样 - 我应该如何实施?

【问题讨论】:

  • Execute 是否采用单个字典或一系列关键字参数?第一个块中的代码似乎表示前者,但您调用partial 的方式表示后者。此外,是否有完整的可用Execute 方法的列表公开给脚本?
  • @wilusdaman,它实际上需要一个字典。使用partial 的方式是因为存在间歇性的内部和外部方法来构造此字典并对其值进行操作。很遗憾,没有完整的列表。

标签: python xmlrpclib


【解决方案1】:

我最近看了a talk by Raymond Hettinger,这篇文章让我想起了这个帖子,所以我想我会链接到它。我认为这为您在帖子中描述的方法提供了一个很好的论据:

让 API 为您服务

首先,我认为覆盖 __getattr__ 并不像那篇帖子可能让你相信的那样有罪。

class MyAPI(object):
  base_args = {'token': 'foobarauthtoekn'}

  def __init__(self, ip):
    self._api = ServerProxy(ip)

  def _execute(self, method='', **kwargs):
    argdict = {'Method': method}
    argdict.update(MyAPI2.base_args)
    argdict.update(kwargs)
    self._api.Execute(argdict)

  def __getattr__(self, attr):
    return self.__dict__.get(
      attr, partial(self._execute, method=attr))


api = MyAPI('127.0.0.1')

# Execute({'token': ..., 'Method': 'get_users', 'arg1': 'foo' ...}) =
api.get_users(arg1='foo', arg2='bar')

# Execute({'token': ..., 'Method': 'get_data', 'foo': 'stuff' ...}) =
api.get_data(foo='stuff', bar='zxcv')

我喜欢这个,因为它的代码不多,而且它让我们可以更方便地使用Execute(使用关键字 args),而无需依赖内部 API 的支持。

通过元类的特殊行为

另一种方法可以防止我们不得不重写__getattr__,以防我低估了它的错误程度,而且它也可以被认为是更pythonic,因为我们明确列举了我们将提供的方法API 包装器:

class APIMeta(type):
  def __new__(cls, clsname, bases, attrs):

    def exec_generic(name):
      base_args = {'token': 'foobarauthtoekn'}
      def exec_wrapper(self, params):
        args = {'Method': name}
        args.update(base_args)
        args.update(params)
        return self._api.Execute(args)
      return exec_wrapper

    new_attrs = {
      name: val if name.startswith('__') else exec_generic(name)
      for name, val in attrs.items() }

    return super(APIMeta, cls).__new__(cls, clsname, bases, new_attrs)

class MyAPI(object, metaclass=APIMeta):
  def __init__(self, ip):
    self._api = ServerProxy(ip)

  def get_users(self):
    pass

  def get_data(self):
    pass

  # ...

实际上,我不喜欢这样,因为它只不过是一种非常复杂的写作方式:

class MyAPI(object):
  _base_args = {'token': 'foobarauthtoken'}

  def __init__(self, ip):
    self._api = ServerProxy(ip)

  def get_users(self, params):
    argdict = {'Method': 'get_users'}
    argdict.update(MyAPI._base_args)
    argdict.update(params)
    return self._api.Execute(argdict)

  # ...

如果你有很多方法,它会为你节省大量的打字时间,如果你想深入了解或加深对 python 语言的理解,这也是元编程的好习惯。

Here's 两者都有的演示。

【讨论】:

  • 非常感谢。这确实在这一点上纯粹是一个练习问题,我非常想找到一些元类的应用程序。我会尝试这些建议,一旦适合,如果您不介意,我会接受答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-13
  • 2011-05-31
  • 1970-01-01
  • 2021-11-04
相关资源
最近更新 更多