properties - How do you unwrap a python property to get attributes from the getter? - Stack Overflow

admin2025-04-19  0

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.

ref: /@genexu/extracting-the-wrapped-function-from-a-decorator-on-python-call-stack-2ee2e48cdd8e

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 

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.

ref: https://medium/@genexu/extracting-the-wrapped-function-from-a-decorator-on-python-call-stack-2ee2e48cdd8e

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 

Share Improve this question asked Mar 3 at 18:38 dakra137dakra137 1405 bronze badges 3
  • The property itself doesn't have the internal value; that's something stored on the object using the property. It only stores references to functions that can manipulate a given object's state. – chepner Commented Mar 3 at 19:46
  • See docs.python./3/howto/descriptor.html#properties for a description of how properties work, using the descriptor protocol. – chepner Commented Mar 3 at 19:47
  • The reason __get__ seems like a recursive wrapper is that __get__ itself is a function with a __get__ method. (The descriptor how-to linked in my previous comment also discusses how instance methods are implemented via the descriptor protocol.) __get__'s __get__ method is unrelated to the property's __get__ method. – chepner Commented Mar 3 at 21:10
Add a comment  | 

1 Answer 1

Reset to default 1

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/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.

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745077457a283654.html

最新回复(0)