访问 Python 描述符的属性

问题描述 投票:0回答:2

不确定这是否可行。 下面的实现/示例是虚拟的,仅供参考。

我有一个Python 类,Person。每个人都有一个公共名字和一个公共姓氏属性以及相应的私有属性 - 我正在使用描述符模式来管理对底层私有属性的访问。

我使用描述符来计算属性被访问的次数并获取底层结果。

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

从类 Person 的实例中,如何访问

_access_count
或属性
counter

john = Person('John','Smith')
print(john.first) # 'John'
print(john.first.counter) # AttributeError: 'str' object has no attribute 'counter'
python python-descriptors
2个回答
0
投票

正如您在这里看到的:

https://docs.python.org/3/howto/descriptor.html#customized-names

可以选择使用


print(vars(Person)['first']._access_count)

这将为您提供底层对象,而不会触发 get

注意我们在对象的类上调用变量


0
投票

无法通过

john.first.counter
直接访问,因为描述符
__get__
将返回
str
及其完成方式。 有一种方法可以解决这个问题,可以按照this answer修改方法。

    def __get__(self, instance, owner):
        if instance is None:
            return self
        self._access_count += 1
        return getattr(instance, self.attrib_name)

计数器的值可以根据问题通过

type(john).first.counter
Person.first.counter
检索。 请注意,在此实现中,属性访问的计数器在底层对象的所有实例之间共享,因为计数器存储在描述符对象本身上: john = Person('John','Smith') mary = Person('Mary','Jane') print(john.first) print(mary.first) print(type(john).first.counter) print(type(mary).first.counter)

上面现在将输出以下内容:
John
Mary
2
2

如果您希望每个实例都有单独的计数器,则相关计数器也需要存储到实例上,如果两者都需要,可能是这样的:
class AttributeAccessCounter:
    def __init__(self):
        self._access_count = 0

    def __get__(self, instance, owner):
        if instance is None:
            return self
        self._access_count += 1
        setattr(
            instance,
            f'{self.attrib_name}_counter',
            getattr(instance, f'{self.attrib_name}_counter', 0) + 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

    def count(self, instance):
        return getattr(instance, f'{self.attrib_name}_counter', 0)

有了这个新的测试程序:
john = Person('John','Smith')
mary = Person('Mary','Jane')
print(john.first)
print(mary.first)
print(mary.first)
print(Person.first.counter)
print(Person.first.count(john))
print(Person.first.count(mary))

生成以下输出:
John
Mary
Mary
3
1
2


© www.soinside.com 2019 - 2024. All rights reserved.