Python 为什么我可以调用所有这些非';在类中没有显式定义吗?
因此,我正在为vk(相当于欧洲的Facebook)使用python。上的文档包含所有可以使用的API调用,并且包装器能够正确调用它们。例如,要获取用户信息,您可以调用Python 为什么我可以调用所有这些非';在类中没有显式定义吗?,python,oop,methods,vk,Python,Oop,Methods,Vk,因此,我正在为vk(相当于欧洲的Facebook)使用python。上的文档包含所有可以使用的API调用,并且包装器能够正确调用它们。例如,要获取用户信息,您可以调用api.users.get(id)以通过id获取用户。我的问题是:如果users或相应的users.get()方法均未在api对象中定义,包装器如何正确处理此类调用 我知道它涉及到\uuu getattr\uuuuuuuuuuuuuuuuuuuuuuuuu()和\uuuuuuuuuuuuuuuuuuuu调用方法,但我找不到任何关于如
api.users.get(id)
以通过id获取用户。我的问题是:如果users
或相应的users.get()
方法均未在api
对象中定义,包装器如何正确处理此类调用
我知道它涉及到\uuu getattr\uuuuuuuuuuuuuuuuuuuuuuuuu()
和\uuuuuuuuuuuuuuuuuuuu调用方法,但我找不到任何关于如何以这种方式使用它们的好文档
编辑
api
对象通过api=vk.api(id、电子邮件、密码)
实例化users.get()
与api
对象无关。对于用户
,您是对的,如果没有定义这样的成员,那么肯定有一些逻辑在内部。因此,正如您在文档\uuuu getattr\uuuu
中看到的那样
当属性查找未在常规位置找到属性时调用(即,它不是实例属性,也不是在类树中为self找到的属性)。name是属性名
因此,确切地说,由于没有为api
类定义用户
,因此调用\uuuu getattr\uuuu
时将'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
假设注释是正确的,并且api
是中定义的APISession
的一个实例,那么这是一个有趣的迷宫:
所以首先要访问api.user
APISession
没有这样的属性,因此它调用\uuuu getattr\uuuu('user')
,定义为:
def __getattr__(self, method_name):
return APIMethod(self, method_name)
因此,这构造了一个APIMethod(api,'user')
。现在,您希望在绑定到api.users
的APIMethod(api,'user')
上调用方法get
,但是APIMethod
没有get
方法,因此它调用自己的\ugetattr\uuuuu('get')
来确定要做什么:
def __getattr__(self, method_name):
return APIMethod(self._api_session, self._method_name + '.' + method_name)
这将返回一个APIMethod(api,'users.get')
,然后调用APIMethod
类的\uuuuuuuuuuuuuu
方法调用该方法,即:
def __call__(self, **method_kwargs):
return self._api_session(self._method_name, **method_kwargs)
因此,它试图返回api('users.get')
,但是api
是一个APISession
对象,因此它调用此类的\uuuu call\uuuu
方法,定义为(为了简单起见去掉错误处理):
然后它调用一个方法请求('users.get'),如果你遵循这个方法,它实际上会执行一个POST请求,一些数据作为响应返回,然后返回。让我们一起来看看这个,好吗
api
要执行api.users.get()
,Python首先必须知道api
。由于您的实例化,它确实知道这一点:它是一个局部变量,包含
api.users
然后,它必须知道api.users
。Python首先查看api
实例的成员、其类的成员(APISession
)以及该类的超类的成员(对于APISession
,仅查看object
)。在这些地方找不到名为users
的成员,它会在这些地方查找名为\uuuu getattr\uuuu
的成员函数。它将在实例上找到它,因为APISession
Python然后使用'users'
(到目前为止缺少的成员的名称)调用它,并使用函数的返回值,就好像它是该成员一样。所以
api.users
相当于
api.__getattr__('users')
APIMethod(api, 'users')
APIMethod(api, 'users' + '.' + 'get')
让我看看
哦。所以
api.users # via api.__getattr__('users')
相当于
api.__getattr__('users')
APIMethod(api, 'users')
APIMethod(api, 'users' + '.' + 'get')
创建一个新实例
api
和'users'
作为该实例的\u api\u session
和\u method\u name
成员。我想这是有道理的
api.users.get
Python仍然没有执行我们的语句。它需要知道api.users.get()
才能这样做。与以前相同的游戏会重复,只是在api中。用户
对象而不是api
对象中:在APIMethod
实例api上找不到成员方法get()
,也找不到成员get
。用户
指向,也不指向其类或超类,因此Python转而使用\uuuu getattr\uuuu
方法,该方法对此类执行一些特殊的操作:
def __getattr__(self, method_name):
return APIMethod(self._api_session, self._method_name + '.' + method_name)
同一类的新实例!让我们插入api.users
的实例成员,然后
api.users.get
相当于
api.__getattr__('users')
APIMethod(api, 'users')
APIMethod(api, 'users' + '.' + 'get')
因此,我们将在api.user.get
\u apisession
中也有api
对象,并在其\u方法名称中有字符串'users.get'
api.users.get()
(注意()
)
因此,api.users.get
是一个对象。要调用它,Python必须假装它是一个函数,或者更具体地说,是一个api.users
的方法。它通过调用api.users.get
来实现,如下所示:
def __call__(self, **method_kwargs):
return self._api_session(self._method_name, **method_kwargs)
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])
让我们来解决这个问题:
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')
因此,Python现在调用api
对象,就好像它是一个函数一样<代码>\uuuuuuuuuuuuuuuuuuu
再次呼叫营救,看起来像这样:
def __call__(self, **method_kwargs):
return self._api_session(self._method_name, **method_kwargs)
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])
现在,这是很多错误处理。对于这个分析,我只对快乐之路感兴趣。我只对“快乐之路”的结果感兴趣(以及我们是如何做到的)。让我们开始吧
数据从哪里来?它是response.text的第一个元素