str.upper 和 list.append 都是函数。
str.upper 接受一个参数。
>>> str.upper('test')
'TEST'
list.append 接受两个参数。
>>> my_list = []
>>> list.append(my_list, 1)
>>> my_list
[1]
str.upper 和 list.append(与其他函数一样)也是带有 __get__ 方法的 non-data-descriptors,在这种情况下有两个含义:
- 当您通过点符号(
str.upper、list.append)通过类访问函数时,会调用函数的__get__ 方法(即string.upper.__get__ 和list.append.__get__),但它只返回函数本身。
- 当您通过实例(
my_string.upper、my_list.append)访问函数时,函数的__get__ 方法被调用,它会返回一个新的可调用对象像原始函数一样,但是“在点前面”的任何内容都自动作为第一个参数传递。 .
这就是为什么在调用 my_string.upper() 时需要传递 1 - 1 = 0 参数和在调用 my_list.append(1) 时需要传递 2 - 1 = 1 参数的原因。
>>> 'my_string'.upper()
'MY_STRING'
>>>
>>> my_list = []
>>> my_list.append(1)
>>> my_list
[1]
您甚至可以通过显式调用 __get__ 并将要绑定的参数(点之前的内容)作为其参数来获取这些修改后的可调用对象(方法)。
>>> my_string = 'my_string'
>>> upper_maker = str.upper.__get__(my_string)
>>> upper_maker()
'MY_STRING'
>>>
>>> my_list = []
>>> appender = list.append.__get__(my_list)
>>> appender(1)
>>> my_list
[1]
最后,这是一个简短的示例,演示描述符实例如何检测它们是通过其所有者类还是通过实例进行访问的。
class Descriptor:
def __get__(self, instance, owner_class):
if instance is None:
print('accessed through class')
# list.append.__get__ would return list.append here
else:
print('accessed through instance')
# list.append.__get__ would build a new callable here
# that takes one argument x and that internally calls
# list.append(instance, x)
class Class:
attribute = Descriptor()
Class.attribute # prints 'accessed through class'
instance = Class()
instance.attribute # prints 'accessed through instance'