请参阅下面的简单示例:
class Celsius(object):
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
def __call__(self):
print('__call__ called')
class Temperature(object):
celsius = Celsius()
def __init__(self):
self.celsius1 = Celsius()
T = Temperature()
print('T.celsius:', T.celsius)
print('T.celsius1:', T.celsius1)
output
T.celsius: 0.0
T.celsius1: <__main__.Celsius object at 0x023544F0>
我想知道为什么他们有不同的输出。
我知道T.celsius
会致电__get__
并T.celsius1
致电__call__
。
答案 0 :(得分:8)
不同之处在于第一个属性是类属性,而第二个属性是实例属性。
根据the documentation,如果实现至少实现Descriptor
个方法中的第一个(__get__
,__set__
和__delete__
)的对象 class 属性,访问时将调用其__get__
方法。 实例属性不是这种情况。您可以了解更多from the howto。
当对象像函数一样被调用时,对象的__call__
方法才起作用:
>>> class Foo:
... def __call__(self):
... return "Hello there!"
...
>>> f = Foo()
>>> f()
'Hello There!'
答案 1 :(得分:2)
以下方法仅适用于类的实例 包含该方法(所谓的描述符类)出现在 所有者类(描述符必须在所有者的类中 字典或其父母之一的班级字典。)
因此描述符(即实现__get__
,__set__
或__delete__
的对象)必须是类的成员,而不是实例。
随着以下变化:
Temperature.celsius2 = Celsius()
T = Temperature()
print('T.celsius:', T.celsius)
print('T.celsius1:', T.celsius1)
print('T.celsius2:', T.celsius2)
输出结果为:
T.celsius: 0.0
T.celsius1: <__main__.Celsius object at 0x7fab8c8d0fd0>
T.celsius2:, 0.0
更多链接:
答案 2 :(得分:2)
T.celcius
作为类Temperature
的属性,因此它会按预期返回T.celcius的__get__
方法的结果。
T.celsius1
是实例T
的一个属性,因此它返回变量本身,因为仅为新样式对象或类调用描述符。
如果您要执行__call__
,则会使用T.celsius()
方法。
答案 3 :(得分:0)
如果您尝试这样做,您将看到相同的结果。
print('T.celsius:', T.celsius)
print('T.celsius1:', T.celsius1.value)
答案 4 :(得分:0)
正如其他人所说,描述符实例应该用作类属性。
class Temperature(object):
celsius = Celsius()
def __init__(self):
self.celsius1 = Celsius()
如果您希望self.celsius1
拥有自定义行为,请覆盖__getattr__
方法:
class Temperature(object):
celsius = Celsius()
def __getattr__(self, name):
if name == 'celsius1':
return ...