2012-03-28 59 views
10

请参见下面的简单的例子:混淆在python __get__和__call__

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

没有什么可遗憾,这是一个很好的问题。 – brice 2012-03-28 10:41:12

+0

所以,你自己回答了你自己的问题。 – Denis 2012-03-28 10:43:43

+1

非常好的问题。我必须检查文档。相当混乱的行为,海事组织。 – codeape 2012-03-28 10:52:21

回答

2

documentation:当类包含方法(所谓的描述符类) 的一个实例出现在一个 所有者

下面的方法只适用类(描述符必须位于其所有者的类 字典中或父类之一的类字典中)。

所以描述符必须是一个类,不是一个实例的成员(即实施__get____set____delete__即对象)。

随着followinging变化:

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 

更多链接:

8

的差异在于这样的事实的第一属性是一个属性而第二个是一个实例属性。

作为每the documentation,如果实现至少第一的Descriptor方法(__get____set____delete__)的对象中一个属性被保持在被访问时其__get__方法将被调用。 实例属性的情况并非如此。您可以了解更多from the howto

对象的__call__方法仅进场当对象被调用的功能等:

>>> class Foo: 
... def __call__(self): 
...  return "Hello there!" 
... 
>>> f = Foo() 
>>> f() 
'Hello There!' 
+0

请在(和为什么)调用对象作为函数时编写。例如, – IProblemFactory 2012-03-28 12:35:29

+0

@ProblemFactory [高级装饰器用法](http://wiki.python.org/moin/PythonDecorators)。将对象传递给'map','filter'或'reduce'函数以获得高级功能。智能动态调度。代码检测。猴子补丁。名单继续,继续,... – brice 2012-03-28 12:38:58

2

T.celcius作为类Temperature的属性,以便它返回T的__get__方法的结果如预期的那样。

T.celsius1是实例T的一个属性,因此它仅返回变量本身,因为描述符仅针对新样式对象或类进行调用。

如果您要做T.celsius(),将使用__call__方法。

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