代码之家  ›  专栏  ›  技术社区  ›  Matt Anderson

python属性查找没有任何描述符魔法?

  •  6
  • Matt Anderson  · 技术社区  · 16 年前

    在我编写的代码中,我已经开始更广泛地使用python描述符协议。通常,我希望使用默认的python查找魔法,但有时我发现我希望获得描述符对象本身,而不是其结果 __get__ 方法。想要知道描述符的类型,或者描述符中存储的访问状态,等等。

    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
    

    Denis的回答给了我一个约定,在描述符类中使用它来获取描述符对象本身。但是,我有一个完整的描述符类的类层次结构,我不想开始 每一个 __得到__ 具有样板的函数

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

    为了避免这种情况,我让描述符类树的根继承自以下内容:

    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)
    
    4 回复  |  直到 16 年前
        1
  •  13
  •   Community Mohan Dere    9 年前

    大多数描述符在仅作为实例属性访问时执行其工作。因此,在类中访问它时,可以方便地返回自身:

    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 这样做。

        2
  •  3
  •   uayebforever    9 年前

    这个 inspect 库提供了一个无需任何描述符即可检索属性的函数: inspect.getattr_static .

    https://docs.python.org/3/library/inspect.html#fetching-attributes-statically

    (这是一个老问题,但当我试图记住如何做这件事时,我不断遇到它,所以我发布了这个答案,以便我能再次找到它!)

        3
  •  0
  •   innov8    10 年前

    上述方法

    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>
    
        4
  •  0
  •   extratype    8 年前

    obj.prop 哪里 type(obj) is C .

    C.prop 通常有效,因为描述符在通过访问时通常会返回自身 C (即,必须 ). 但是 prop 他们没有出席会议 obj , AttributeError 虽然 C.支柱 可能不会。所以最好使用 inspect.getattr_static(obj, 'prop') .

    _PyObject_GenericGetAttrWithDict 在里面 Objects/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 是一个常用对象(例如,不是类)。