def find_descriptor1(instance, attrname):
'''Find the descriptor handling a given attribute, if any.
If the attribute named attrname of the given instance is handled by a
descriptor, this will return the descriptor object handling the attribute.
Otherwise, it will return None.
'''
def hasspecialmethod(obj, name):
return any(name in klass.__dict__ for klass in type(obj).__mro__)
for klass in type(instance).__mro__:
if attrname in klass.__dict__:
descriptor = klass.__dict__[attrname]
if not (hasspecialmethod(descriptor, '__get__') or
hasspecialmethod(descriptor, '__set__') or
hasspecialmethod(descriptor, '__delete__')):
# Attribute isn't a descriptor
return None
if (attrname in instance.__dict__ and
not hasspecialmethod(descriptor, '__set__') and
not hasspecialmethod(descriptor, '__delete__')):
# Would be handled by the descriptor, but the descriptor isn't
# a data descriptor and the object has a dict entry overriding
# it.
return None
return descriptor
return None
def find_descriptor2(obj, name:str):
""" Find the descriptor, if any, an attribute retrieval on an instance would go through
:param obj: instance object
:param name: attribute which would be retrieved
:returns: unbound descriptor ``__get__``, or ``None`` if no descriptor would be used
"""
# lookup attribute in class hierarchy
for base in type(obj).__mro__:
base_dict = vars(base)
if name in base_dict:
# we look for descriptor interface on value's class, not value itself
value_clazz = type(base_dict[name])
descr = getattr(value_clazz, '__get__', None)
if descr:
# data descriptor?
if (hasattr(value_clazz, '__set__') or hasattr(value_clazz, "__delete__")):
return descr
# instance variable can be used instead?
if hasattr(obj, '__dict__') and name in vars(obj):
return
# non-data descriptor?
# if None, it indicates a class variable
return descr
class WeirdDescriptor:
def __set_name__(self, owner, name):
self.name = name
def __set__(self, instance, value):
print('setter invoked!')
instance.__dict__[self.name] = value
class Foo:
x = WeirdDescriptor()
x = Foo()
x.x = 2
print(x.x)
print(find_descriptor1(x, 'x'))
print(find_descriptor2(x, 'x'))
ZGVmIGZpbmRfZGVzY3JpcHRvcjEoaW5zdGFuY2UsIGF0dHJuYW1lKToKICAgICcnJ0ZpbmQgdGhlIGRlc2NyaXB0b3IgaGFuZGxpbmcgYSBnaXZlbiBhdHRyaWJ1dGUsIGlmIGFueS4KCiAgICBJZiB0aGUgYXR0cmlidXRlIG5hbWVkIGF0dHJuYW1lIG9mIHRoZSBnaXZlbiBpbnN0YW5jZSBpcyBoYW5kbGVkIGJ5IGEKICAgIGRlc2NyaXB0b3IsIHRoaXMgd2lsbCByZXR1cm4gdGhlIGRlc2NyaXB0b3Igb2JqZWN0IGhhbmRsaW5nIHRoZSBhdHRyaWJ1dGUuCiAgICBPdGhlcndpc2UsIGl0IHdpbGwgcmV0dXJuIE5vbmUuCiAgICAnJycKICAgIGRlZiBoYXNzcGVjaWFsbWV0aG9kKG9iaiwgbmFtZSk6CiAgICAgICAgcmV0dXJuIGFueShuYW1lIGluIGtsYXNzLl9fZGljdF9fIGZvciBrbGFzcyBpbiB0eXBlKG9iaikuX19tcm9fXykKICAgIGZvciBrbGFzcyBpbiB0eXBlKGluc3RhbmNlKS5fX21yb19fOgogICAgICAgIGlmIGF0dHJuYW1lIGluIGtsYXNzLl9fZGljdF9fOgogICAgICAgICAgICBkZXNjcmlwdG9yID0ga2xhc3MuX19kaWN0X19bYXR0cm5hbWVdCiAgICAgICAgICAgIGlmIG5vdCAoaGFzc3BlY2lhbG1ldGhvZChkZXNjcmlwdG9yLCAnX19nZXRfXycpIG9yCiAgICAgICAgICAgICAgICAgICAgaGFzc3BlY2lhbG1ldGhvZChkZXNjcmlwdG9yLCAnX19zZXRfXycpIG9yCiAgICAgICAgICAgICAgICAgICAgaGFzc3BlY2lhbG1ldGhvZChkZXNjcmlwdG9yLCAnX19kZWxldGVfXycpKToKICAgICAgICAgICAgICAgICMgQXR0cmlidXRlIGlzbid0IGEgZGVzY3JpcHRvcgogICAgICAgICAgICAgICAgcmV0dXJuIE5vbmUKICAgICAgICAgICAgaWYgKGF0dHJuYW1lIGluIGluc3RhbmNlLl9fZGljdF9fIGFuZAogICAgICAgICAgICAgICAgbm90IGhhc3NwZWNpYWxtZXRob2QoZGVzY3JpcHRvciwgJ19fc2V0X18nKSBhbmQKICAgICAgICAgICAgICAgIG5vdCBoYXNzcGVjaWFsbWV0aG9kKGRlc2NyaXB0b3IsICdfX2RlbGV0ZV9fJykpOgogICAgICAgICAgICAgICAgIyBXb3VsZCBiZSBoYW5kbGVkIGJ5IHRoZSBkZXNjcmlwdG9yLCBidXQgdGhlIGRlc2NyaXB0b3IgaXNuJ3QKICAgICAgICAgICAgICAgICMgYSBkYXRhIGRlc2NyaXB0b3IgYW5kIHRoZSBvYmplY3QgaGFzIGEgZGljdCBlbnRyeSBvdmVycmlkaW5nCiAgICAgICAgICAgICAgICAjIGl0LgogICAgICAgICAgICAgICAgcmV0dXJuIE5vbmUKICAgICAgICAgICAgcmV0dXJuIGRlc2NyaXB0b3IKICAgIHJldHVybiBOb25lCgpkZWYgZmluZF9kZXNjcmlwdG9yMihvYmosIG5hbWU6c3RyKToKICAgICIiIiBGaW5kIHRoZSBkZXNjcmlwdG9yLCBpZiBhbnksIGFuIGF0dHJpYnV0ZSByZXRyaWV2YWwgb24gYW4gaW5zdGFuY2Ugd291bGQgZ28gdGhyb3VnaAogICAgCiAgICAgICAgOnBhcmFtIG9iajogaW5zdGFuY2Ugb2JqZWN0CiAgICAgICAgOnBhcmFtIG5hbWU6IGF0dHJpYnV0ZSB3aGljaCB3b3VsZCBiZSByZXRyaWV2ZWQKICAgICAgICA6cmV0dXJuczogdW5ib3VuZCBkZXNjcmlwdG9yIGBgX19nZXRfX2BgLCBvciBgYE5vbmVgYCBpZiBubyBkZXNjcmlwdG9yIHdvdWxkIGJlIHVzZWQKICAgICIiIgogICAgIyBsb29rdXAgYXR0cmlidXRlIGluIGNsYXNzIGhpZXJhcmNoeQogICAgZm9yIGJhc2UgaW4gdHlwZShvYmopLl9fbXJvX186CiAgICAgICAgYmFzZV9kaWN0ID0gdmFycyhiYXNlKSAKICAgICAgICBpZiBuYW1lIGluIGJhc2VfZGljdDoKICAgICAgICAgICAgIyB3ZSBsb29rIGZvciBkZXNjcmlwdG9yIGludGVyZmFjZSBvbiB2YWx1ZSdzIGNsYXNzLCBub3QgdmFsdWUgaXRzZWxmCiAgICAgICAgICAgIHZhbHVlX2NsYXp6ID0gdHlwZShiYXNlX2RpY3RbbmFtZV0pCiAgICAgICAgICAgIGRlc2NyID0gZ2V0YXR0cih2YWx1ZV9jbGF6eiwgJ19fZ2V0X18nLCBOb25lKQogICAgICAgICAgICBpZiBkZXNjcjoKICAgICAgICAgICAgICAgICMgZGF0YSBkZXNjcmlwdG9yPwogICAgICAgICAgICAgICAgaWYgKGhhc2F0dHIodmFsdWVfY2xhenosICdfX3NldF9fJykgb3IgaGFzYXR0cih2YWx1ZV9jbGF6eiwgIl9fZGVsZXRlX18iKSk6CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGRlc2NyCiAgICAgICAgICAgICAgICAjIGluc3RhbmNlIHZhcmlhYmxlIGNhbiBiZSB1c2VkIGluc3RlYWQ/CiAgICAgICAgICAgICAgICBpZiBoYXNhdHRyKG9iaiwgJ19fZGljdF9fJykgYW5kIG5hbWUgaW4gdmFycyhvYmopOgogICAgICAgICAgICAgICAgICAgIHJldHVybgogICAgICAgICAgICAjIG5vbi1kYXRhIGRlc2NyaXB0b3I/CiAgICAgICAgICAgICMgaWYgTm9uZSwgaXQgaW5kaWNhdGVzIGEgY2xhc3MgdmFyaWFibGUKICAgICAgICAgICAgcmV0dXJuIGRlc2NyCgpjbGFzcyBXZWlyZERlc2NyaXB0b3I6CiAgICBkZWYgX19zZXRfbmFtZV9fKHNlbGYsIG93bmVyLCBuYW1lKToKICAgICAgICBzZWxmLm5hbWUgPSBuYW1lCiAgICBkZWYgX19zZXRfXyhzZWxmLCBpbnN0YW5jZSwgdmFsdWUpOgogICAgICAgIHByaW50KCdzZXR0ZXIgaW52b2tlZCEnKQogICAgICAgIGluc3RhbmNlLl9fZGljdF9fW3NlbGYubmFtZV0gPSB2YWx1ZQoKY2xhc3MgRm9vOgogICAgeCA9IFdlaXJkRGVzY3JpcHRvcigpCgp4ID0gRm9vKCkKeC54ID0gMgpwcmludCh4LngpCnByaW50KGZpbmRfZGVzY3JpcHRvcjEoeCwgJ3gnKSkKcHJpbnQoZmluZF9kZXNjcmlwdG9yMih4LCAneCcpKQ==