Python类@property:使用setter但回避getter?

Python类@property:使用setter但回避getter?,python,class,properties,timing,Python,Class,Properties,Timing,在python类中,@property是一个很好的装饰器,它避免使用显式的setter和getter函数。然而,它的开销是“经典”类函数的2-5倍。在我的例子中,这在设置属性的情况下是很好的,与设置属性时需要进行的处理相比,开销是微不足道的 但是,我在获取属性时不需要处理。它总是“归还自己的财产”。是否有一种优雅的方法可以使用setter而不使用getter,而不需要使用不同的内部变量 仅举个例子,下面的类具有属性“var”,它指的是内部变量“\u var”。调用“var”比调用“_-var”需

在python类中,@property是一个很好的装饰器,它避免使用显式的setter和getter函数。然而,它的开销是“经典”类函数的2-5倍。在我的例子中,这在设置属性的情况下是很好的,与设置属性时需要进行的处理相比,开销是微不足道的

但是,我在获取属性时不需要处理。它总是“归还自己的财产”。是否有一种优雅的方法可以使用setter而不使用getter,而不需要使用不同的内部变量

仅举个例子,下面的类具有属性“var”,它指的是内部变量“\u var”。调用“var”比调用“_-var”需要更长的时间,但是如果开发者和用户都可以使用“var”而不必跟踪“_-var”,那就太好了


对于那些感兴趣的人,在我的电脑上调用“var”平均需要204纳秒,而调用“u var”平均需要44纳秒。

在这种情况下,不要使用
属性。
property
对象是一个数据描述符,这意味着对
instance.var
的任何访问都将调用该描述符,Python永远不会在实例本身上查找属性

您有两个选项:使用钩子或构建一个只实现
\uuuuu set\uuuu
的描述符

使用
\uuuu setattr\uuuuu()钩子
现在,在读取
.var
时使用常规属性查找,但在分配给
.var
时,将调用
\uuuu setattr\uuuuu
方法,让您截取
值并根据需要对其进行调整

演示:

设置描述符 setter描述符只能截取变量赋值:

class SetterProperty(object):
    def __init__(self, func, doc=None):
        self.func = func
        self.__doc__ = doc if doc is not None else func.__doc__
    def __set__(self, obj, value):
        return self.func(obj, value)

class Foo(object):
    @SetterProperty
    def var(self, value):
        print 'Setting var!'
        self.__dict__['var'] = value
注意我们需要如何分配实例
。\uuuu dict\uuuu
属性,以防止再次调用setter

演示:


属性
python文档:

输出:

ok
Traceback (most recent call last):
  File "Untitled.py", line 15, in <module>
    print c.var
AttributeError: unreadable attribute
ok
回溯(最近一次呼叫最后一次):
文件“Untitled.py”,第15行,在
打印c.var
AttributeError:无法读取的属性

如果接受答案的setter描述符自己设置属性,那么它可能会更方便:

setter描述符(alt.)
类设置器:
定义初始化(self、func、doc=None):
self.func=func
self.\uuuuuu doc\uuuuuu=doc或func.\uuuuuu doc__
定义设置(自身、对象、值):
obj.\uuuu dict\uuuu[self.func.\uuuuuu name\uuuu]=self.func(obj,value)
Foo类:
@塞特
def变量(自身,值):
打印('设置变量!')
#对收到的值进行验证和/或操作
如果不是isinstance(值,str):
raise VALUERROR('var'必须是字符串')
value=value.capitalize()
#返回属性值
返回值
演示:

>f=Foo()
>>>f.var=‘垃圾邮件’
设置变量!
>>>f.var='ham'
设置变量!
>>>f.var
“火腿”
>>>f.var='biggles'
设置变量!
>>>f.var
“比格斯”
>>>f.var=3
ValueError:`var`必须是字符串
关于@WeizhongTu的答案

class MyClass(object):
    def __init__(self):
        self._var = None

    # only setter
    def var(self, newValue):
        self._var = newValue

    var = property(None, var)


c = MyClass()
c.var = 3
print ('ok')
print (c.var)
很好,除了使变量不可设置之外

一个类似的解决方案,但保留getter是

而不是

var = property(None, var)

不,没有这样的办法。一旦您使用了数据描述符,相同名称的实例属性将不受尊重。非常感谢您的回复!我最终使用了setattr钩子,因为它只允许对“myInstance.attrib+=5”这样的值进行操作。我可以补充一点,如果setattr是在扩展另一个类的类中设置的,那么它不会被覆盖。就性能而言,与@property setter相比,它增加了大约x3的开销,但当然,获取属性值要快得多,这一点更为重要。快速提问。如果你把self.\uuu dict.\uuu['var']=value
移动到描述符的
\uuu set.\ucode>方法作为
obj.\uu dict.\uu['var']=value
,它会起作用吗?@madpysicator:当然
self
obj
在那里引用同一个对象。更一般地说,您可以执行
obj.\uuu dict\uuuu[self.func.\uu name\uuuu]=value
?当通过
def
定义带注释的方法时,我是否正确地期望这一点起作用?同样相关:这并不能解决问题,因为我仍然希望属性是“可获取的”。只是我不想在获取它时产生任何处理开销。但有趣的解决方案用于其他目的@JonasLindeløv是的,这可能对某些人有用,所以我在这里发布了我的答案。有什么方法可以让我把它用作装饰。我的意思是,我不使用
var=property(None,var)
,而是使用decorator@WeizhongTu您可以通过将
var=property(None,var)
替换为
var=property(lambda x:x.。\u-var,var)
var=property(lambda-self:getattr(self,'u-var',None),var)使属性可读
使您无需定义
self.\u var
。但通常,最好的选择可能是使用setter,而不是定义
self.\u var
。这样,如果没有设置值,对象将引发一个
AttributeError
,这通常是预期的。
>>> f = Foo()
>>> f.var = 'spam'
Setting var!
>>> f.var = 'ham'
Setting var!
>>> f.var
'ham'
>>> f.var = 'biggles'
Setting var!
>>> f.var
'biggles'
class MyClass(object):
    def __init__(self):
        self._var = None

    # only setter
    def var(self, newValue):
        self._var = newValue

    var = property(None, var)


c = MyClass()
c.var = 3
print ('ok')
print (c.var)
ok
Traceback (most recent call last):
  File "Untitled.py", line 15, in <module>
    print c.var
AttributeError: unreadable attribute
class MyClass(object):
    def __init__(self):
        self._var = None

    # only setter
    def var(self, newValue):
        self._var = newValue

    var = property(None, var)


c = MyClass()
c.var = 3
print ('ok')
print (c.var)
var = property(lambda self: self._var, var)
var = property(None, var)