From "outside", how can I access attributes in a property's getter function whether by unwrapping it or some other way?
In a python property, its __get__
function seems to be a wrapper of a wrapper of a wrapper ...
Using inspect.unwrap on the __get__
function returns another wrapper, not the getter. In fact, what unwrap returns seems to be the exact same object and id as what was unwrapped. See the MRE and its output below.
Yes, I know that in simple cases I can get the attribute through
classinstance.__class__.propertygetterfunction._privateattribute
In my real situation, not the MRE below, the getter had been dynamically defined and is referenced only in synthesizing the property. Once created, there are no other references to the getter besides what is buried in the property.
What is the path from aclass.aproperty.__get__
to the getter and its attributes?
In the following MRE example, the getter contains one attribute containing how may times the getter has been called, and another containing the property's internal value.
from inspect import unwrap
class propholder:
def __init__(self):
self.__class__.propgetter._usecount=0
def propgetter(self):
self.__class__.propgetter._usecount=self.__class__.propgetter._usecount +1
print(f"getcount= {self.__class__.propgetter._usecount}")
return self.__class__.propgetter._pvalue # set by the setter
def propsetter(self, v):
self.__class__.propgetter._pvalue=v
# function to delete _age attribute
def propdeleter(self):
del self.__class__.propgetter
del self.__class__.propsetter
del self.__class__.propdeleter
aprop = property(propgetter, propsetter, propdeleter)
ap = propholder()
for i in [10,11,12]:
ap.aprop=i
print(f" ap.aprop={ap.aprop} ")
prop_get=ap.__class__.aprop.__get__
u=unwrap(prop_get)
print( f"\n prop_get=ap.__class__.aprop.__get__ = {prop_get} of type {prop_get.__class__} at {id(prop_get)} ")
print( f" u=unwrap(prop_get) = {u} of type {u} at {id(u)} ")
un=u
for i in range(2,6):
u=unwrap(u)
print( f" u=unwrap(u) = {u} of type {u} at {id(u)} ")
Results:
getcount= 1
ap.aprop=10
getcount= 2
ap.aprop=11
getcount= 3
ap.aprop=12
prop_get=ap.__class__.aprop.__get__ = <method-wrapper '__get__' of property object at 0x72b6af94d940> of type <class 'method-wrapper'> at 126128955449232
u=unwrap(prop_get) = <method-wrapper '__get__' of property object at 0x72b6af94d940> of type <method-wrapper '__get__' of property object at 0x72b6af94d940> at 126128955449232
u=unwrap(u) = <method-wrapper '__get__' of property object at 0x72b6af94d940> of type <method-wrapper '__get__' of property object at 0x72b6af94d940> at 126128955449232
u=unwrap(u) = <method-wrapper '__get__' of property object at 0x72b6af94d940> of type <method-wrapper '__get__' of property object at 0x72b6af94d940> at 126128955449232
u=unwrap(u) = <method-wrapper '__get__' of property object at 0x72b6af94d940> of type <method-wrapper '__get__' of property object at 0x72b6af94d940> at 126128955449232
u=unwrap(u) = <method-wrapper '__get__' of property object at 0x72b6af94d940> of type <method-wrapper '__get__' of property object at 0x72b6af94d940> at 126128955449232
ap.__class__.aprop.__get__
is a method of ap.__class__.aprop
, so when you access the __get__
attribute of ap.__class__.aprop
you invoke the MethodType
descriptor, which stores the object that the method is bound to as the __self__
attribute. In this case, the object it's bound to is ap.__class__.aprop
, a property
descriptor, which stores the getter function in the fget
attribute.
So in your demo code, the getter function can be obtained from ap.__class__.aprop.__get__
with:
ap.__class__.aprop.__get__.__self__.fget
And its private attribute can be accessed with:
ap.__class__.aprop.__get__.__self__.fget._usecount
Demo: https://ideone.com/FWQdnH
Note that inspect.unwrap
does nothing more than following the chain of __wrapped__
attributes stored by the functools.update_wrapper
function or any wrapper function following the same protocol. It does not magically unwrap functions stored in any other attributes.