所以我在python中使用API wrapper for vk,欧洲的Facebook等价物。 vk site上的文档包含可以使用的所有API调用,并且包装器能够正确调用它们。例如,要获取用户的信息,您可以调用api.users.get(id)
按ID获取用户。我的问题是:当users
对象中未定义users.get()
或相应的api
方法时,包装器如何正确处理此类调用?
我知道它涉及__getattr__()
和__call__()
方法,但我找不到任何关于如何以这种方式使用它们的好文档。
修改
api
对象通过api = vk.API(id, email, password)
答案 0 :(得分:2)
让我们一起走过这个,不是吗?
api
要执行api.users.get()
,Python首先必须知道api
。由于你的实例化,它 知道它:它是一个持有APISession
实例的局部变量。
api.users
然后,它必须知道api.users
。 Python首先查看api
实例的成员,在其类(APISession
)的成员和该类的成员中。超类(object
的情况下只有APISession
)。如果在这些地方找不到名为users
的成员,它会在同一个地方寻找名为__getattr__
的成员函数。它会在实例上找到它,因为APISession
has an (instance) member function of this name。
'users'
(迄今为止缺少的成员的名称)调用它,并使用函数的返回值,就像它是该成员一样。所以
api.users
相当于
api.__getattr__('users')
让我们看看what that returns。
def __getattr__(self, method_name):
return APIMethod(self, method_name)
喔。所以
api.users # via api.__getattr__('users')
相当于
APIMethod(api, 'users')
创建新的APIMethod
实例。
api
和'users'
end up作为该实例的_api_session
和_method_name
成员。我猜是有道理的。
api.users.get
Python仍然没有执行我们的声明。它需要知道api.users.get()
这样做。与之前相同的游戏重复,这次仅在api.users
对象而不是api
对象中:get()
上没有成员get
1}}实例APIMethod
指向它的类或超类,因此Python转向api.users
方法,对于这个类,它做了一些特殊的事情:
__getattr__
同一个班级的新实例!让我们插入 def __getattr__(self, method_name):
return APIMethod(self._api_session, self._method_name + '.' + method_name)
的实例成员和
api.users
等同于
api.users.get
因此,APIMethod(api, 'users' + '.' + 'get')
api
中的api.user.get
对象和_apisession
中的字符串'users.get'
也会有。{/ p >
_method_name
(请注意api.users.get()
)所以()
是一个对象。要调用它,Python必须假装它是一个函数,或者更具体地说,是api.users.get
的方法。它是这样做的,而是调用api.users
' s __call__
method,它看起来像这样:
api.users.get
让我们解决这个问题:
def __call__(self, **method_kwargs):
return self._api_session(self._method_name, **method_kwargs)
所以现在Python正在调用api.users.get()
# is equivalent to
api.users.get.__call__() # no arguments, because we passed none to `get()`
# will return
api.users.get._api_session(api.users.get._method_name)
# which is
api('users.get')
对象,就像它是一个函数一样。再次api
救援,this time看起来像这样:
__call__
现在,这是一个很多错误处理。对于这种分析,我只对快乐的道路感兴趣。我只对快乐路径的结果(以及我们如何到达那里)感兴趣。所以让我们开始at the result。
def __call__(self, method_name, **method_kwargs):
response = self.method_request(method_name, **method_kwargs)
response.raise_for_status()
# there are may be 2 dicts in 1 json
# for example: {'error': ...}{'response': ...}
errors = []
error_codes = []
for data in json_iter_parse(response.text):
if 'error' in data:
error_data = data['error']
if error_data['error_code'] == CAPTCHA_IS_NEEDED:
return self.captcha_is_needed(error_data, method_name, **method_kwargs)
error_codes.append(error_data['error_code'])
errors.append(error_data)
if 'response' in data:
for error in errors:
warnings.warn(str(error))
return data['response']
if AUTHORIZATION_FAILED in error_codes: # invalid access token
self.access_token = None
self.get_access_token()
return self(method_name, **method_kwargs)
else:
raise VkAPIMethodError(errors[0])
return data['response']
来自哪里?它是data
被解释为JSON的第一个元素,其中包含一个'响应'宾语。因此,似乎从我们获得的response.text
对象中,我们正在提取实际的响应部分。
response
对象来自哪里? <{3}}来自
response
对于我们所关心的一切,这是一个简单的was returned电话,并不需要任何花哨的后备。 (当然,它的实施,在某些层面上,可能。)
答案 1 :(得分:1)
假设评论是正确的,api
是this particular commit中定义的APISession
的实例,那么这是一个有趣的迷宫:
首先,您要访问api.user
。 APISession
没有此类属性,因此它会调用__getattr__('user')
,其定义为:
def __getattr__(self, method_name):
return APIMethod(self, method_name)
所以这构造了一个APIMethod(api,'user')
。现在,您要调用绑定到get
的{{1}}上的方法APIMethod(api,'user')
,但api.users
没有APIMethod
方法,所以它调用自己的get
来弄清楚要做什么:
__getattr__('get')
这会返回一个def __getattr__(self, method_name):
return APIMethod(self._api_session, self._method_name + '.' + method_name)
,然后调用APIMethod(api,'users.get')
类的__call__
方法,该方法是:
APIMethod
所以这会尝试返回def __call__(self, **method_kwargs):
return self._api_session(self._method_name, **method_kwargs)
,但api('users.get')
是一个api
对象,所以它会调用此类的APISession
方法,定义为(去掉错误)处理简单):
__call__
然后它调用method_request(&#39; users.get&#39;),如果你遵循该方法实际上发出了一个POST请求,并且一些数据作为响应返回,然后返回。
答案 2 :(得分:0)
users.get()
与api
对象无关。至于users
,你是对的,如果没有定义这样的成员,那么__getattr__
内肯定有一些逻辑。正如您在文档__getattr__
中看到的那样......
当属性查找未在通常位置找到属性时调用(即,它不是实例属性,也不是在类树中找到自己)。 name是属性名称。
确切地说,由于users
的类没有定义api
,因此调用__getattr__
并将'users'
作为name
传递参数。然后,最可能是动态的,取决于传递的参数,正在为users
组件构造并返回一个对象,该对象将负责以get()
方法的类似方式定义或处理。
要了解整个想法,请尝试以下操作:
class A(object):
def __init__(self):
super(A, self).__init__()
self.defined_one = 'I am defined inside A'
def __getattr__(self, item):
print('getting attribute {}'.format(item))
return 'I am ' + item
a = A()
>>> print(a.some_item) # this will call __getattr__ as some_item is not defined
getting attribute some_item
I am some_item
>>> print(a.and_another_one) # this will call __getattr__ as and_another_one is not defined
getting attribute and_another_one
I am and_another_one
>>> print(a.defined_one) # this will NOT call __getattr__ as defined_one is defined in A
I am defined inside A