Not sure if this is feasible or not. The implementation/example below is dummy, FYI.
I have a Python class, Person. Each person has a public first name and a public last name attribute with corresponding private attributes - I'm using a descriptor pattern to manage access to the underlying private attributes.
I am using the descriptor to count the number of times the attribute is accessed as well as obtain the underlying result.
class AttributeAccessCounter:
def __init__(self):
self._access_count = 0
def __get__(self, instance, owner):
self._access_count += 1
return getattr(instance, self.attrib_name)
def __set_name__(self, obj, name):
self.attrib_name = f'_{name}'
@property
def counter(self):
return self._access_count
class Person:
first = AttributeAccessCounter()
last = AttributeAccessCounter()
def __init__(self, first, last):
self._first = first
self._last = last
From an instance of the class Person, how can I access the _access_count
or property counter
?
john = Person('John','Smith')
print(john.first) # 'John'
print(john.first.counter) # AttributeError: 'str' object has no attribute 'counter'
Currently you don't differentiate when the descriptor is accessed through the instance or through the class itself. property
does this for example. It gives you the descriptor object when you access it through the class.
You can do the same:
def __get__(self, instance, owner):
if instance is None:
return self
self._access_count += 1
return getattr(instance, self.attrib_name)
Now the counter
property of the descriptor class can be accessed.
However there is another problem pointed out by @user2357112 in the comment. You store this count on descriptor and it's shared between different instances. You can't really tell first
attribute of which instance of Person
is accessed n times.
To solve that, if you still want to store it in the descriptor object, one way is to use a dictionary and call e method for getting the count.
Here is the complete code:
from collections import defaultdict
class AttributeAccessCounter:
def __init__(self):
self._access_counts = defaultdict(int)
def __get__(self, instance, owner):
if instance is None:
return self
self._access_counts[instance] += 1
return getattr(instance, self.attrib_name)
def __set_name__(self, obj, name):
self.attrib_name = f"_{name}"
def count(self, obj):
return self._access_counts[obj]
class Person:
first = AttributeAccessCounter()
last = AttributeAccessCounter()
def __init__(self, first, last):
self._first = first
self._last = last
john = Person("John", "Smith")
foo = Person("foo", "bar")
print(Person.first.count(john)) # 0
print(john.first) # 'John'
print(Person.first.count(john)) # 1
print(Person.first.count(foo)) # 0