سؤال

ولقد بدأت في استخدام بروتوكول الثعبان اصف أكثر على نطاق واسع في كود لقد تم كتابة. عادة، الثعبان بحث السحر الافتراضي هو ما أريد أن يحدث، ولكن في بعض الأحيان أجد أريد الحصول على الكائن واصف نفسه بدلا نتائج طريقة __get__ لها. يريد أن يعرف نوع واصف، أو دولة الوصول المخزنة في واصف، أو شيء سومساك.

وكتبت رمز أدناه على المشي مساحات في ما أعتقد هو الترتيب الصحيح، والعودة السمة الخام بغض النظر عن ما إذا كان واصف أم لا. أنا مندهش على الرغم من أنني لا يمكن العثور على وظيفة المضمنة أو شيء في المكتبة القياسية للقيام بذلك - الرقم الأول يجب أن يكون هناك وأنا فقط لم يلاحظ أو غوغليد لمصطلح البحث الصحيح.

هل هناك وظيفة في مكان ما في توزيع الثعبان أن يفعل هذا بالفعل (أو ما شابه)؟

وشكرا!

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

وتحرير الأربعاء، 28 أكتوبر 2009.

والجواب دينيس وأعطاني اتفاقية لاستخدامها في الفصول الدراسية واصف لي للحصول على واصف الكائنات أنفسهم. ولكن، كان لي التسلسل الهرمي فئة كاملة من فئات واصف، وأنا لم أكن أريد أن أبدأ <م> كل وظيفة __get__ مع النمطي

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)
هل كانت مفيدة؟

المحلول

ومعظم اصفات القيام بعملهم عند الوصول إليها كما المثال السمة فقط. حتى أنها مريحة للعودة نفسه عندما يكون الوصول إليها لفئة:

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>

والوصول إلى واصف يمكن أن تكون مفيدة عندما تحتاج إلى حد أنه في فئة فرعية، ولكن هناك <لأ href = "https://stackoverflow.com/questions/101268/hidden-features-of-python/1631763#1631763" > أفضل طريقة للقيام بذلك.

نصائح أخرى

وتوفر المكتبة inspect وظيفة لاسترداد سمة من دون أي السحر واصف: inspect.getattr_static

وثائق: https://docs.python.org /3/library/inspect.html#fetching-attributes-statically

و(هذا هو السؤال القديم، ولكن أظل القادمة عبر عندما أحاول أن أتذكر كيفية القيام بذلك، لذلك أنا نشر هذا الجواب حتى أتمكن من العثور عليه مرة أخرى!)

والأسلوب أعلاه

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>

ودعونا نقول أننا نريد للحصول على واصف لobj.prop حيث type(obj) is C.

وC.prop عادة ما تعمل لواصف عادة ما يعود نفسه عند الوصول إليها عبر C (أي، منضما إلى C). ولكن قد يؤدي C.prop واصف في metaclass لها. إذا كانت prop يست موجودة في obj، فإن obj.prop رفع AttributeError في حين قد لا C.prop. لذلك فمن الأفضل استخدام 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') يعود واصف في بنفس الطريقة عندما يستخدم سي بايثون ذلك في obj.prop إذا obj هو كائن المعتاد (وليس الطبقة، على سبيل المثال).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top