关于python中__get__和__call__的困惑

时间:2012-03-28 10:34:44

标签: python

请参阅下面的简单示例:

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__

5 个答案:

答案 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)

来自documentation

  

以下方法仅适用于类的实例   包含该方法(所谓的描述符类)出现在   所有者类(描述符必须在所有者的类中   字典或其父母之一的班级字典。)

因此描述符(即实现__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)

What is the purpose of self?

答案 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 ...