2009-10-27 63 views
6

我已经开始在我写的代码中更广泛地使用python描述符协议。通常情况下,默认的python查找魔术就是我想要发生的事情,但有时候我发现我想获取描述符对象本身,而不是其__get__方法的结果。想知道描述符的类型,或描述符中存储的访问状态,或者其他的东西。python属性查找没有任何描述符魔术?

我写了下面的代码来遍历我认为是正确的顺序的命名空间,并返回raw属性,而不管它是否是描述符。我很惊讶,虽然我无法在标准库中找到内置函数或其他东西,但我认为它必须在那里,我只是没有注意到它,或者搜索了正确的搜索词。

是否有功能在python分布的某处已经做了这个(或类似的东西)?

谢谢!

from inspect import isdatadescriptor 

def namespaces(obj): 
    obj_dict = None 
    if hasattr(obj, '__dict__'): 
     obj_dict = object.__getattribute__(obj, '__dict__') 

    obj_class = type(obj) 
    return obj_dict, [t.__dict__ for t in obj_class.__mro__] 

def getattr_raw(obj, name): 
    # get an attribute in the same resolution order one would normally, 
    # but do not call __get__ on the attribute even if it has one 
    obj_dict, class_dicts = namespaces(obj) 

    # look for a data descriptor in class hierarchy; it takes priority over 
    # the obj's dict if it exists 
    for d in class_dicts: 
     if name in d and isdatadescriptor(d[name]): 
      return d[name] 

    # look for the attribute in the object's dictionary 
    if obj_dict and name in obj_dict: 
     return obj_dict[name] 

    # look for the attribute anywhere in the class hierarchy 
    for d in class_dicts: 
     if name in d: 
      return d[name] 

    raise AttributeError 

编辑周三,10月28日,2009年

丹尼斯的回答给了我一个约定,我的描述符类使用来获取描述对象本身。但是,我不得不描述符类的整个类层次,我不想开始与样板

def __get__(self, instance, instance_type): 
    if instance is None: 
     return self 
    ... 

为了避免这种情况每__get__功能,我做了描述符类树的根继承以下内容:

def decorate_get(original_get): 
    def decorated_get(self, instance, instance_type): 
     if instance is None: 
      return self 
     return original_get(self, instance, instance_type) 
    return decorated_get 

class InstanceOnlyDescriptor(object): 
    """All __get__ functions are automatically wrapped with a decorator which 
    causes them to only be applied to instances. If __get__ is called on a 
    class, the decorator returns the descriptor itself, and the decorated 
    __get__ is not called. 
    """ 
    class __metaclass__(type): 
     def __new__(cls, name, bases, attrs): 
      if '__get__' in attrs: 
       attrs['__get__'] = decorate_get(attrs['__get__']) 
      return type.__new__(cls, name, bases, attrs) 
+0

有时候你想要描述符对象?这违反了描述符的核心期望:它们应该看起来像属性。为什么要打破这种基本的期望?为什么要这样做?为什么创造如此复杂的东西 – 2009-10-27 01:34:41

+0

我在做什么并不觉得*对我来说很复杂,但我想你可以说我正在试验这个设计。 在我目前的特殊情况下,我有一个描述符,它返回游戏中武器的强度。该值是描述符状态(武器强度)和实例(船舶的健康状况)的函数。有不同种类的武器;通常我只是想要价值结果,但在少数情况下,我需要知道它是什么样的武器 - 描述符的类型。 如果一个描述符的方法不是描述符协议的一部分,而你想调用它们呢? – 2009-10-27 02:06:52

回答

11

大多数描述符仅在作为实例属性进行访问时才会完成其工作。所以它的方便返回自己当它类访问:

class FixedValueProperty(object): 
    def __init__(self, value): 
     self.value = value 
    def __get__(self, inst, cls): 
     if inst is None: 
      return self 
     return self.value 

这可以让你获得描述自己:

>>> class C(object): 
...  prop = FixedValueProperty('abc') 
... 
>>> o = C() 
>>> o.prop 
'abc' 
>>> C.prop 
<__main__.FixedValueProperty object at 0xb7eb290c> 
>>> C.prop.value 
'abc' 
>>> type(o).prop.value 
'abc' 

注意,这适用于(大多数?)内置描述过:当你需要它的程度在子类

>>> class C(object): 
...  @property 
...  def prop(self): 
...   return 'abc' 
... 
>>> C.prop 
<property object at 0xb7eb0b6c> 
>>> C.prop.fget 
<function prop at 0xb7ea36f4> 

访问描述可能是有用的,但有一个better way做到这一点。

+0

没有意识到;很高兴知道。函数本身就是一个例外,但是可能是唯一的例外。将不得不捅内置的描述符。 – 2009-10-27 14:53:11

+0

虽然它并没有完全回答我的问题,但您的回答确实帮助我解决了潜在的问题。我会接受它。 – 2009-10-28 16:05:39

+0

函数是这种模式的一个例外(我假设你谈论方法)?不,“c.method”从描述中返回一个绑定的方法,而“C.method”返回一个未绑定的方法。这是一样的模式。 – u0b34a0f6ae 2009-12-09 14:19:46

0

上述方法

class FixedValueProperty(object): 
    def __init__(self, value): 
     self.value = value 
    def __get__(self, inst, cls): 
     if inst is None: 
      return self 
     return self.value 

是一个伟大的方法,只要你控制房地产的代码,但也有一些情况下,比如当属性是由其他人,这里又控制库的一部分方法很有用。这种替代方法在其他情况下也很有用,例如实现对象映射,如问题中所述的漫游名称空间或其他专业库。

考虑一类具有一个简单的属性:

class ClassWithProp: 

    @property 
    def value(self): 
     return 3 
>>>test=ClassWithProp() 
>>>test.value 
3 
>>>test.__class__.__dict__.['value'] 
<property object at 0x00000216A39D0778> 

当从容器对象类字典,在“描述魔法”绕过访问。还要注意的是,如果我们将属性赋值给一个新的类变量,它的行为就像具有'描述符魔术'的原始类型,但是如果赋给一个实例变量,该属性的行为与任何普通对象一样,并且也绕过'描述符魔术'。

>>> test.__class__.classvar = test.__class__.__dict__['value'] 
>>> test.classvar 
3 
>>> test.instvar = test.__class__.__dict__['value'] 
>>> test.instvar 
<property object at 0x00000216A39D0778> 
0

假设我们想要得到obj.prop的描述符,其中type(obj) is C

C.prop通常起作用,因为当通过C(即,绑定到C)访问时,描述符通常返回自身。但C.prop可能触发其元类中的描述符。如果obj中不存在prop,obj.prop将增加AttributeErrorC.prop可能不会。所以最好使用inspect.getattr_static(obj, 'prop')

如果你不满意的是,这里有一个CPython的特定方法(从_PyObject_GenericGetAttrWithDictObjects/object.c):

import ctypes, _ctypes 

_PyType_Lookup = ctypes.pythonapi._PyType_Lookup 
_PyType_Lookup.argtypes = (ctypes.py_object, ctypes.py_object) 
_PyType_Lookup.restype = ctypes.c_void_p 

def type_lookup(ty, name): 
    """look for a name through the MRO of a type.""" 
    if not isinstance(ty, type): 
     raise TypeError('ty must be a type') 

    result = _PyType_Lookup(ty, name) 
    if result is None: 
     raise AttributeError(name) 

    return _ctypes.PyObj_FromPtr(result) 

type_lookup(type(obj), 'prop')返回以同样的方式描述符时CPython中使用它在obj.prop如果obj是通常的对象(例如不是类)。