【问题标题】:Checking if property is settable/deletable检查属性是否可设置/可删除
【发布时间】:2011-08-31 11:00:18
【问题描述】:

如何检查一个属性在 Python 中是否可设置或可删除?

目前为止我发现的最好的是

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

【问题讨论】:

  • 一个好问题!而且您的解决方案看起来是最好的解决方案。
  • 我更愿意说 obj.__class__.prop_name.fset 而不是你的 type(obj).__dict__["prop_name"].fset,但我认为两者都是有效的,我同意 BasicWolf 的观点,即你的答案可能是最好的。

标签: python properties


【解决方案1】:

这是一个很好的案例,您应该订阅“请求宽恕比请求许可更容易”的理念,并且只需在属性不可可设置/可删除的情况下处理异常。

try:
    x.prop = 42
except AttributeError:
    pass

【讨论】:

  • 好答案。我考虑过这一点,但我会自动从属性构建 UI,并使用它们的“可设置性”来决定设置 ItemIsEditable 标志。我想我可以与x.prop = x.prop 核对或更改我的设计。
  • @Neil:如果您编写了有问题的代码并且知道说x.prop = x.prop 总是安全的,那么检查就可以了。但是,属性设置器可能会做一些意想不到的事情,例如访问数据库或增加计数器或以其他方式更改系统的内部状态,因此除非没有其他方法,否则我会避免调用它们。您在问题中提到的解决方案对我来说似乎是最好的。
【解决方案2】:

我认为没有任何方法可以不尝试就预先知道。您无法确定某个对象是否具有奇怪的 __setattr__ 或类似名称,这会破坏您尝试使用的抽象。

【讨论】:

    【解决方案3】:

    以下程序测试三个函数,旨在确定类或实例属性是否支持 CRUD 操作。类或实例是can_* 函数的第一个参数,第二个参数是应该检查的属性的名称。类型检查是自动完成的,以确保函数按预期使用。请注意,这仅适用于使用来自 builtins 模块的 property 类创建的属性。

    #! /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()
    

    【讨论】:

    • 问题中已经给出了这种方法。但是,正如 Daenyth 在他的回答中指出的那样,它通常不起作用。
    • 这可能是真的,但根据您使用的代码,它可能会在特定情况下工作——这对某些人来说可能已经足够了。
    • 是的,没错。这种解决方案是我最终在我的代码中完成的。
    • 如果您提到此解决方案仅适用于使用 property 装饰器实现的属性,我会支持它。
    • 完成。不幸的是,没有像 Cat Plus Plus 建议的那样更强大的解决方案。该提案的唯一问题是它可能会无意中修改基础数据。
    猜你喜欢
    • 1970-01-01
    • 2015-04-25
    • 2018-10-01
    • 2014-05-05
    • 2018-09-09
    • 2014-02-11
    • 1970-01-01
    • 2020-05-23
    • 1970-01-01
    相关资源
    最近更新 更多