Проверка, может ли свойство настраиваться/удаляться

Как проверить, является ли свойство настраиваемым или удаляемым в Python?

Лучшее, что я нашел до сих пор,

type(obj).__dict__["prop_name"].fset is not None

Ответы

Ответ 1

Это хороший случай, когда вы должны подписаться на философию "Это проще просить прощения, чем разрешение", и просто обрабатывать исключение, если свойство не может быть установлено/удалено.

try:
    x.prop = 42
except AttributeError:
    pass

Ответ 2

Я не думаю, что есть какой-то способ узнать себя, не пытаясь. Вы не можете точно знать, есть ли у объекта странный __setattr__ или похожий, который разрушит абстракцию, которую вы пытаетесь использовать.

Ответ 3

Следующая программа проверяет три функции, предназначенные для выяснения того, поддерживает ли свойство класса или экземпляра операции CRUD. Класс или экземпляр является первым аргументом функций can_*, а вторым аргументом является имя свойства, которое должно быть проверено. Проверка типов выполняется автоматически, чтобы гарантировать, что функции используются, как ожидалось. Обратите внимание, что это предназначено только для работы со свойствами, созданными с помощью класса property из модуля builtins.

#! /usr/bin/env python3


def main():
    for kind in Test, TestG, TestS, TestGS, TestD, TestGD, TestSD, TestGSD:
        print(kind.__name__, 'Class')
        print('  can_get:', can_get(kind, 'data'))
        print('  can_set:', can_set(kind, 'data'))
        print('  can_del:', can_del(kind, 'data'))
        print()
        instance = kind('Hello, world!')
        print(kind.__name__, 'Instance')
        print('  can_get:', can_get(instance, 'data'))
        print('  can_set:', can_set(instance, 'data'))
        print('  can_del:', can_del(instance, 'data'))
        print()


def can_get(obj, key):
    return _get_property(obj, key).fget is not None


def can_set(obj, key):
    return _get_property(obj, key).fset is not None


def can_del(obj, key):
    return _get_property(obj, key).fdel is not None


def _get_property(obj, key):
    if not isinstance(obj, type):
        obj = type(obj)
    pro = vars(obj).get(key)
    if not isinstance(pro, property):
        raise TypeError('{.__name__}.{} is not a property'.format(obj, key))
    return pro


class Test:

    def __init__(self, value):
        self.__data = value

    def get_data(self):
        return self.__data

    def set_data(self, value):
        self.__data = value

    def del_data(self):
        del self.__data

    data = property()


class TestG(Test):

    data = property(fget=Test.get_data)


class TestS(Test):

    data = property(fset=Test.set_data)


class TestGS(Test):

    data = property(fget=Test.get_data, fset=Test.set_data)


class TestD(Test):

    data = property(fdel=Test.del_data)


class TestGD(Test):

    data = property(fget=Test.get_data, fdel=Test.del_data)


class TestSD(Test):

    data = property(fset=Test.set_data, fdel=Test.del_data)


class TestGSD(Test):

    data = property(fget=Test.get_data, fset=Test.set_data, fdel=Test.del_data)


if __name__ == '__main__':
    main()