Python @property 装饰器如何工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17330160/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does the @property decorator work?
提问by ashim
I would like to understand how the built-in function property
works. What confuses me is that property
can also be used as a decorator, but it only takes arguments when used as a built-in function and not when used as a decorator.
我想了解内置函数的property
工作原理。让我感到困惑的是,property
它也可以用作装饰器,但它仅在用作内置函数时才接受参数,而在用作装饰器时则不接受。
This example is from the documentation:
这个例子来自文档:
class C(object):
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
property
's arguments are getx
, setx
, delx
and a doc string.
property
的论点是getx
,setx
,delx
和文档字符串。
In the code below property
is used as decorator. The object of it is the x
function, but in the code above there is no place for an object function in the arguments.
在下面的代码中property
用作装饰器。它的对象是x
函数,但在上面的代码中,参数中没有对象函数的位置。
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
And, how are the x.setter
and x.deleter
decorators created?
I am confused.
而且,装饰器x.setter
和x.deleter
装饰器是如何创建的?我很迷惑。
采纳答案by Martijn Pieters
The property()
function returns a special descriptor object:
该property()
函数返回一个特殊的描述符对象:
>>> property()
<property object at 0x10ff07940>
It is this object that has extramethods:
正是这个对象有额外的方法:
>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>
These act as decorators too. They return a new property object:
这些充当装饰过。他们返回一个新的属性对象:
>>> property().getter(None)
<property object at 0x10ff079f0>
that is a copy of the old object, but with one of the functions replaced.
这是旧对象的副本,但替换了其中一个功能。
Remember, that the @decorator
syntax is just syntactic sugar; the syntax:
请记住,@decorator
语法只是语法糖;语法:
@property
def foo(self): return self._foo
really means the same thing as
真的是一样的意思
def foo(self): return self._foo
foo = property(foo)
so foo
the function is replaced by property(foo)
, which we saw above is a special object. Then when you use @foo.setter()
, what you are doing is call that property().setter
method I showed you above, which returns a new copy of the property, but this time with the setter function replaced with the decorated method.
所以foo
函数被替换为property(foo)
,我们在上面看到的是一个特殊的对象。然后,当您使用 时@foo.setter()
,您所做的是调用property().setter
我上面向您展示的那个方法,它返回该属性的新副本,但这次将 setter 函数替换为装饰方法。
The following sequence also creates a full-on property, by using those decorator methods.
以下序列还通过使用这些装饰器方法创建了一个完整的属性。
First we create some functions and a property
object with just a getter:
首先我们创建一些函数和一个property
只有 getter的对象:
>>> def getter(self): print('Get!')
...
>>> def setter(self, value): print('Set to {!r}!'.format(value))
...
>>> def deleter(self): print('Delete!')
...
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True
Next we use the .setter()
method to add a setter:
接下来我们使用该.setter()
方法添加一个setter:
>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True
Last we add a deleter with the .deleter()
method:
最后,我们使用以下.deleter()
方法添加删除器:
>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True
Last but not least, the property
object acts as a descriptor object, so it has .__get__()
, .__set__()
and .__delete__()
methods to hook into instance attribute getting, setting and deleting:
最后但并非最不重要的一点是,该property
对象充当描述符对象,因此它具有.__get__()
,.__set__()
和.__delete__()
挂钩到实例属性获取、设置和删除的方法:
>>> class Foo: pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!
The Descriptor Howto includes a pure Python sample implementationof the property()
type:
Descriptor Howto 包含以下类型的纯 Python 示例实现property()
:
class Property: "Emulate PyProperty_Type() in Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel if doc is None and fget is not None: doc = fget.__doc__ self.__doc__ = doc def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") return self.fget(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError("can't set attribute") self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError("can't delete attribute") self.fdel(obj) def getter(self, fget): return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.__doc__)
class Property: "Emulate PyProperty_Type() in Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel if doc is None and fget is not None: doc = fget.__doc__ self.__doc__ = doc def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") return self.fget(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError("can't set attribute") self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError("can't delete attribute") self.fdel(obj) def getter(self, fget): return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.__doc__)
回答by J0HN
Documentation saysit's just a shortcut for creating readonly properties. So
文档说这只是创建只读属性的快捷方式。所以
@property
def x(self):
return self._x
is equivalent to
相当于
def getx(self):
return self._x
x = property(getx)
回答by glglgl
The first part is simple:
第一部分很简单:
@property
def x(self): ...
is the same as
是相同的
def x(self): ...
x = property(x)
- which, in turn, is the simplified syntax for creating a
property
with just a getter.
- 反过来,它是
property
仅使用 getter创建 a 的简化语法。
The next step would be to extend this property with a setter and a deleter. And this happens with the appropriate methods:
下一步是使用 setter 和 deleter 扩展此属性。这发生在适当的方法中:
@x.setter
def x(self, value): ...
returns a new property which inherits everything from the old x
plus the given setter.
返回一个新属性,该属性继承了旧属性x
和给定 setter 的所有内容。
x.deleter
works the same way.
x.deleter
工作方式相同。
回答by AlexG
Here is a minimal example of how @property
can be implemented:
这是一个如何@property
实现的最小示例:
class Thing:
def __init__(self, my_word):
self._word = my_word
@property
def word(self):
return self._word
>>> print( Thing('ok').word )
'ok'
Otherwise word
remains a method instead of a property.
否则word
仍然是一个方法而不是一个属性。
class Thing:
def __init__(self, my_word):
self._word = my_word
def word(self):
return self._word
>>> print( Thing('ok').word() )
'ok'
回答by Bill Moore
This following:
这如下:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
Is the same as:
是相同的:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
def _x_set(self, value):
self._x = value
def _x_del(self):
del self._x
x = property(_x_get, _x_set, _x_del,
"I'm the 'x' property.")
Is the same as:
是相同的:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
def _x_set(self, value):
self._x = value
def _x_del(self):
del self._x
x = property(_x_get, doc="I'm the 'x' property.")
x = x.setter(_x_set)
x = x.deleter(_x_del)
Is the same as:
是相同的:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
x = property(_x_get, doc="I'm the 'x' property.")
def _x_set(self, value):
self._x = value
x = x.setter(_x_set)
def _x_del(self):
del self._x
x = x.deleter(_x_del)
Which is the same as :
这与以下内容相同:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
回答by nvd
A property can be declared in two ways.
可以通过两种方式声明属性。
- Creating the getter, setter methods for an attribute and then passing these as argument to propertyfunction
- Using the @propertydecorator.
- 为属性创建 getter、setter 方法,然后将它们作为参数传递给属性函数
- 使用@property装饰器。
You can have a look at few examples I have written about properties in python.
你可以看看我写的关于python 属性的几个例子。
回答by Leo Skhrnkv
I read all the posts here and realized that we may need a real life example. Why, actually, we have @property?
So, consider a Flask app where you use authentication system.
You declare a model User in models.py
:
我阅读了这里的所有帖子,并意识到我们可能需要一个现实生活中的例子。为什么,实际上,我们有@property?因此,请考虑使用身份验证系统的 Flask 应用程序。您在models.py
以下位置声明模型用户:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
...
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
In this code we've "hidden" attribute password
by using @property
which triggers AttributeError
assertion when you try to access it directly, while we used @property.setter to set the actual instance variable password_hash
.
在这段代码中,我们password
通过使用@property
whichAttributeError
在您尝试直接访问它时触发断言来“隐藏”属性,而我们使用 @property.setter 来设置实际的实例变量password_hash
。
Now in auth/views.py
we can instantiate a User with:
现在auth/views.py
我们可以实例化一个用户:
...
@auth.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
if form.validate_on_submit():
user = User(email=form.email.data,
username=form.username.data,
password=form.password.data)
db.session.add(user)
db.session.commit()
...
Notice attribute password
that comes from a registration form when a user fills the form. Password confirmation happens on the front end with EqualTo('password', message='Passwords must match')
(in case if you are wondering, but it's a different topic related Flask forms).
password
当用户填写表单时注意来自注册表单的属性。密码确认发生在前端EqualTo('password', message='Passwords must match')
(如果您想知道,但这是与 Flask 表单相关的不同主题)。
I hope this example will be useful
我希望这个例子会有用
回答by Acecool
Here is another example:
这是另一个例子:
##
## Python Properties Example
##
class GetterSetterExample( object ):
## Set the default value for x ( we reference it using self.x, set a value using self.x = value )
__x = None
##
## On Class Initialization - do something... if we want..
##
def __init__( self ):
## Set a value to __x through the getter / setter... Since __x is defined above, this doesn't need to be set...
self.x = 1234
return None
##
## Define x as a property, ie a getter - All getters should have a default value arg, so I added it - it will not be passed in when setting a value, so you need to set the default here so it will be used..
##
@property
def x( self, _default = None ):
## I added an optional default value argument as all getters should have this - set it to the default value you want to return...
_value = ( self.__x, _default )[ self.__x == None ]
## Debugging - so you can see the order the calls are made...
print( '[ Test Class ] Get x = ' + str( _value ) )
## Return the value - we are a getter afterall...
return _value
##
## Define the setter function for x...
##
@x.setter
def x( self, _value = None ):
## Debugging - so you can see the order the calls are made...
print( '[ Test Class ] Set x = ' + str( _value ) )
## This is to show the setter function works.... If the value is above 0, set it to a negative value... otherwise keep it as is ( 0 is the only non-negative number, it can't be negative or positive anyway )
if ( _value > 0 ):
self.__x = -_value
else:
self.__x = _value
##
## Define the deleter function for x...
##
@x.deleter
def x( self ):
## Unload the assignment / data for x
if ( self.__x != None ):
del self.__x
##
## To String / Output Function for the class - this will show the property value for each property we add...
##
def __str__( self ):
## Output the x property data...
print( '[ x ] ' + str( self.x ) )
## Return a new line - technically we should return a string so it can be printed where we want it, instead of printed early if _data = str( C( ) ) is used....
return '\n'
##
##
##
_test = GetterSetterExample( )
print( _test )
## For some reason the deleter isn't being called...
del _test.x
Basically, the same as the C( object ) example except I'm using x instead... I also don't initialize in __init- ... well.. I do, but it can be removed because __x is defined as part of the class....
基本上,与 C( object ) 示例相同,只是我使用的是x 代替...我也没有在 __init 中初始化-...嗯...我这样做,但它可以被删除,因为 __x 被定义为一部分班级的……
The output is:
输出是:
[ Test Class ] Set x = 1234
[ Test Class ] Get x = -1234
[ x ] -1234
and if I comment out the self.x = 1234 in initthen the output is:
如果我在init 中注释掉 self.x = 1234那么输出是:
[ Test Class ] Get x = None
[ x ] None
and if I set the _default = None to _default = 0 in the getter function ( as all getters should have a default value but it isn't passed in by the property values from what I've seen so you can define it here, and it actually isn't bad because you can define the default once and use it everywhere ) ie: def x( self, _default = 0 ):
如果我在 getter 函数中将 _default = None 设置为 _default = 0(因为所有 getter 都应该有一个默认值,但它不是由我所看到的属性值传入的,所以你可以在这里定义它,和它实际上还不错,因为您可以定义一次默认值并在任何地方使用它)即: def x( self, _default = 0 ):
[ Test Class ] Get x = 0
[ x ] 0
Note: The getter logic is there just to have the value be manipulated by it to ensure it is manipulated by it - the same for the print statements...
注意:getter 逻辑只是为了让值被它操纵以确保它被它操纵 - 打印语句也是如此......
Note: I'm used to Lua and being able to dynamically create 10+ helpers when I call a single function and I made something similar for Python without using properties and it works to a degree, but, even though the functions are being created before being used, there are still issues at times with them being called prior to being created which is strange as it isn't coded that way... I prefer the flexibility of Lua meta-tables and the fact I can use actual setters / getters instead of essentially directly accessing a variable... I do like how quickly some things can be built with Python though - for instance gui programs. although one I am designing may not be possible without a lot of additional libraries - if I code it in AutoHotkey I can directly access the dll calls I need, and the same can be done in Java, C#, C++, and more - maybe I haven't found the right thing yet but for that project I may switch from Python..
注意:我已经习惯了 Lua 并且能够在调用单个函数时动态创建 10 个以上的帮助程序,并且我在不使用属性的情况下为 Python 创建了类似的东西,并且它在一定程度上起作用,但是,即使函数是在之前创建的正在使用中,有时在创建之前调用它们仍然存在问题,这很奇怪,因为它不是那样编码的......我更喜欢 Lua 元表的灵活性以及我可以使用实际的 setter/getter 的事实而不是直接访问变量...我确实喜欢用 Python 构建某些东西的速度 - 例如 gui 程序。虽然我正在设计的一个库如果没有很多额外的库可能是不可能的——如果我在 AutoHotkey 中编码,我可以直接访问我需要的 dll 调用,同样可以在 Java、C#、C++ 中完成,
Note: The code output in this forum is broken - I had to add spaces to the first part of the code for it to work - when copy / pasting ensure you convert all spaces to tabs.... I use tabs for Python because in a file which is 10,000 lines the filesize can be 512KB to 1MB with spaces and 100 to 200KB with tabs which equates to a massive difference for file size, and reduction in processing time...
注意:此论坛中的代码输出已损坏 - 我必须在代码的第一部分添加空格才能使其工作 - 复制/粘贴时确保将所有空格转换为制表符......我使用 Python 制表符,因为在一个 10,000 行的文件,文件大小可以是 512KB 到 1MB 的空格和 100 到 200KB 的制表符,这相当于文件大小的巨大差异,并减少了处理时间......
Tabs can also be adjusted per user - so if you prefer 2 spaces width, 4, 8 or whatever you can do it meaning it is thoughtful for developers with eye-sight deficits.
还可以为每个用户调整选项卡 - 因此,如果您更喜欢 2 个空格宽度、4、8 或任何您可以做的事情,这意味着它对于视力有缺陷的开发人员来说是周到的。
Note: All of the functions defined in the class aren't indented properly because of a bug in the forum software - ensure you indent it if you copy / paste
注意:由于论坛软件中的错误,类中定义的所有函数都没有正确缩进 - 如果复制/粘贴,请确保缩进
回答by Divyanshu Rawat
Let's start with Python decorators.
让我们从 Python 装饰器开始。
A Python decorator is a function that helps to add some additional functionalities to an already defined function.
Python 装饰器是一个函数,它有助于向已定义的函数添加一些附加功能。
In Python, everything is an object. Functions in Python are first-class objects which means that they can be referenced by a variable, added in the lists, passed as arguments to another function etc.
在 Python 中,一切都是对象。Python 中的函数是一等对象,这意味着它们可以被变量引用、添加到列表中、作为参数传递给另一个函数等。
Consider the following code snippet.
考虑以下代码片段。
def decorator_func(fun):
def wrapper_func():
print("Wrapper function started")
fun()
print("Given function decorated")
# Wrapper function add something to the passed function and decorator
# returns the wrapper function
return wrapper_func
def say_bye():
print("bye!!")
say_bye = decorator_func(say_bye)
say_bye()
# Output:
# Wrapper function started
# bye
# Given function decorated
Here, we can say that decorator function modified our say_hello function and added some extra lines of code in it.
在这里,我们可以说装饰器函数修改了我们的 say_hello 函数并在其中添加了一些额外的代码行。
Python syntax for decorator
装饰器的 Python 语法
def decorator_func(fun):
def wrapper_func():
print("Wrapper function started")
fun()
print("Given function decorated")
# Wrapper function add something to the passed function and decorator
# returns the wrapper function
return wrapper_func
@decorator_func
def say_bye():
print("bye!!")
say_bye()
Let's Concluded everything than with a case scenario, but before that let's talk about some oops priniciples.
让我们结束所有而不是案例场景,但在此之前让我们谈谈一些 oops 原则。
Getters and setters are used in many object oriented programming languages to ensure the principle of data encapsulation(is seen as the bundling of data with the methods that operate on these data.)
很多面向对象的编程语言中都使用了 getter 和 setter 来确保数据封装的原则(被视为数据与对这些数据进行操作的方法的捆绑。)
These methods are of course the getter for retrieving the data and the setter for changing the data.
这些方法当然是检索数据的 getter 和更改数据的 setter。
According to this principle, the attributes of a class are made private to hide and protect them from other code.
根据这个原则,类的属性被设为私有以隐藏和保护它们不受其他代码的影响。
Yup, @propertyis basically a pythonic way to use getters and setters.
是的,@property基本上是一种使用 getter 和 setter的Pythonic 方式。
Python has a great concept called property which makes the life of an object-oriented programmer much simpler.
Python 有一个很棒的概念,称为属性,它使面向对象程序员的生活变得更加简单。
Let us assume that you decide to make a class that could store the temperature in degree Celsius.
让我们假设您决定创建一个可以以摄氏度为单位存储温度的类。
class Celsius:
def __init__(self, temperature = 0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
def get_temperature(self):
return self._temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value
Refactored Code, Here is how we could have achieved it with property.
重构代码,这是我们如何通过属性实现它。
In Python, property() is a built-in function that creates and returns a property object.
在 Python 中,property() 是一个内置函数,它创建并返回一个属性对象。
A property object has three methods, getter(), setter(), and delete().
属性对象具有三个方法,getter()、setter() 和 delete()。
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def get_temperature(self):
print("Getting value")
return self.temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self.temperature = value
temperature = property(get_temperature,set_temperature)
Here,
这里,
temperature = property(get_temperature,set_temperature)
could have been broken down as,
可以分解为,
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
Point To Note:
注意事项:
- get_temperature remains a property instead of a method.
- get_temperature 仍然是一个属性而不是一个方法。
Now you can access the value of temperature by writing.
现在您可以通过写入访问温度值。
C = Celsius()
C.temperature
# instead of writing C.get_temperature()
We can further go on and not define names get_temperatureand set_temperatureas they are unnecessary and pollute the class namespace.
我们可以进一步不定义名称get_temperature和set_temperature因为它们是不必要的并且会污染类命名空间。
The pythonic wayto deal with the above problem is to use @property.
处理上述问题的Pythonic 方法是使用@property。
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
@property
def temperature(self):
print("Getting value")
return self.temperature
@temperature.setter
def temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self.temperature = value
Points to Note -
注意事项 -
- A method which is used for getting a value is decorated with "@property".
- The method which has to function as the setter is decorated with "@temperature.setter", If the function had been called "x", we would have to decorate it with "@x.setter".
- We wrote "two" methods with the same name and a different number of parameters "def temperature(self)" and "def temperature(self,x)".
- 用于获取值的方法用“@property”修饰。
- 必须作为setter的方法用“@temperature.setter”装饰,如果函数被称为“x”,我们必须用“@x.setter”装饰它。
- 我们编写了具有相同名称和不同数量参数的“两个”方法“def temperature(self)”和“def temperature(self,x)”。
As you can see, the code is definitely less elegant.
如您所见,代码肯定不那么优雅。
Now,let's talk about one real-life practical scenerio.
现在,让我们来谈谈一个现实生活中的实用场景。
Let's say you have designed a class as follows:
假设您设计了一个类,如下所示:
class OurClass:
def __init__(self, a):
self.x = a
y = OurClass(10)
print(y.x)
Now, let's further assume that our class got popular among clients and they started using it in their programs, They did all kinds of assignments to the object.
现在,让我们进一步假设我们的类在客户中很受欢迎,并且他们开始在他们的程序中使用它,他们对对象进行了各种分配。
And One fateful day, a trusted client came to us and suggested that "x" has to be a value between 0 and 1000, this is really a horrible scenario!
命运的一天,一位值得信赖的客户来找我们,建议“x”必须是 0 到 1000 之间的值,这真是一个可怕的场景!
Due to properties it's easy: We create a property version of "x".
由于属性,这很容易:我们创建了一个“x”的属性版本。
class OurClass:
def __init__(self,x):
self.x = x
@property
def x(self):
return self.__x
@x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
This is great, isn't it: You can start with the simplest implementation imaginable, and you are free to later migrate to a property version without having to change the interface! So properties are not just a replacement for getters and setter!
这很棒,不是吗:您可以从可以想象到的最简单的实现开始,然后您可以自由地迁移到属性版本,而无需更改界面!所以属性不仅仅是 getter 和 setter 的替代品!
You can check this Implementation here
您可以在此处查看此实现
回答by Cleb
Below is another example on how @property
can help when one has to refactor code which is taken from here(I only summarize it below):
下面是另一个示例,说明@property
当必须重构从此处获取的代码时如何提供帮助(我仅在下面对其进行了总结):
Imagine you created a class Money
like this:
想象一下,你创建了一个Money
这样的类:
class Money:
def __init__(self, dollars, cents):
self.dollars = dollars
self.cents = cents
and an user creates a library depending on this class where he/she uses e.g.
并且用户根据他/她使用的这个类创建一个库,例如
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.
Now let's suppose you decide to change your Money
class and get rid of the dollars
and cents
attributes but instead decide to only track the total amount of cents:
现在让我们假设你决定改变你的Money
类并去掉dollars
和cents
属性,而是决定只跟踪美分的总量:
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
If the above mentioned user now tries to run his/her library as before
如果上述用户现在尝试像以前一样运行他/她的库
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
it will result in an error
它会导致错误
AttributeError: 'Money' object has no attribute 'dollars'
AttributeError: 'Money' 对象没有属性 'dollars'
That means that now everyone who relies on your original Money
class would have to change all lines of code where dollars
and cents
are used which can be very painful... So, how could this be avoided? By using @property
!
这意味着现在每个依赖您的原始Money
类的人都必须更改所有使用位置dollars
和cents
使用的代码行,这可能非常痛苦......那么,如何避免这种情况?通过使用@property
!
That is how:
就是那样:
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
# Getter and setter for dollars...
@property
def dollars(self):
return self.total_cents // 100
@dollars.setter
def dollars(self, new_dollars):
self.total_cents = 100 * new_dollars + self.cents
# And the getter and setter for cents.
@property
def cents(self):
return self.total_cents % 100
@cents.setter
def cents(self, new_cents):
self.total_cents = 100 * self.dollars + new_cents
when we now call from our library
当我们现在从我们的图书馆打电话时
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.
it will work as expected and we did not have to change a single line of code in our library! In fact, we would not even have to know that the library we depend on changed.
它将按预期工作,我们无需更改库中的任何一行代码!事实上,我们甚至不必知道我们依赖的库发生了变化。
Also the setter
works fine:
也setter
工作正常:
money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.
money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.
You can use @property
also in abstract classes; I give a minimal example here.
您也@property
可以在抽象类中使用;我在这里举一个最小的例子。