The difference in literal meaning
Both Attribute and property can be translated into attributes. Although their meanings are almost the same in both Chinese and English, there are still some differences. After several times of Google, I found a more reliable explanation:
According to Webster, a property is a characteristic that belongs to a thing’s essential nature and may be used to describe a type or species.
An attribute is a modifier word that serves to limit, identify, particularize, describe, or supplement the meaning of the word it modifies.
Simply put, property is the essential attribute of a class, which can be used to define and describe a category or species; attribute is used to specify the object it describes, which is the specific attribute of the object.
For example: Everyone has a mouth. Some people have a big mouth, which is one of the properties of a person, and a big mouth can only be said to be an attribute of some people.
In this sense, property is a subset of attribute.
Attributes and properties in Python
Back to Python.
Attribute and property are not distinguished in Java, but they are different in Python. The following is an (informal) definition given by Fluent Python (Chapter 19):
Explain separately next.
attribute
All data attributes and methods are attributes.According to the owner of the attribute, it can be divided into class attribute and instance attribute.All attributes of class or instance are stored in their own dict attributes.
E.g:
# Python3
classFoo():
name ='Foo class attribute'
def fn(self):
pass
print('class attribute:', Foo.__dict__)print()
foo =Foo()
foo.name ='foo instance attribute'print('instance attribute:', foo.__dict__)
Output:
class attribute: {‘fn’: <function Foo.fn at 0x7fd135ec8ea0 , … , ‘name’: ‘Foo class attribute’}
instance attribute: {‘name’: ‘foo instance attribute’}
property
Property is to replace the data attribute with setter/getter methods for security reasons, for example, read-only attributes and attribute value legality verification.
Read-only attribute
E.g:
classFoo():
def __init__(self, name):
self.name = name
foo =Foo('I do not want to be changed')print('foo.name = ', foo.name)
foo.name ='Unluckily, I can be changed'print('foo.name = ', foo.name)
Output:
foo.name = I do not want to be changed
foo.name = Unluckily, I can be changed
In the above code, if we only want to expose the name attribute of foo to the outside for reading, but don't want it to be modified, what should we do? Two solutions were listed in the Python definition of read-only attributes. One solution: "through private attributes", in fact, is to use property instead of attribute.
Rewrite the above foo.name into property:
classFoo():
def __init__(self, name):
self.__name = name
@ property
def name(self):return self.__name
foo =Foo('I do not want to be changed')print('foo.name = ', foo.name)
foo.name ='Luckily, I really can not be changed'
Output:
foo.name = I do not want to be changed
---------------------------------------------------------------------------
AttributeError Traceback(most recent call last)<ipython-input-69-101c96ba497e in<module()9 foo =Foo('I do not want to be changed')10print('foo.name = ', foo.name)---11 foo.name ='Luckily, I really can not be changed'
AttributeError: can't set attribute
There are two points to note:
It is true that foo.name cannot be modified by foo.name = …, that is, foo.name is already a read-only attribute.
After changing foo.name from attribute to property, its access method has not changed. In other words, the external interface has not changed. This advantage allows us to write code calmly without having to worry about whether to use property or attribute at the beginning. Because you can use attributes, if necessary, you can modify it at any time without affecting the external code. It is difficult to do this in Java (if it can be done).
Property value legality verification
In the above example, foo.name has only a getter method, which is read-only, but in fact the property is also modifiable. You only need to add a setter method to it. Then the problem is, if the property is also readable and can be modified , Then why bother to rewrite attribute into property?
Imagine a simple shopping-related business scenario. An Item represents a thing purchased by a user, mainly including category, price and quantity attributes:
classItem():
def __init__(self, category, count, price):
self.cat = category
self.count = count
self.price = price
The normal call is similar to this, the price and quantity are both positive numbers:
item = Item(‘Bread’, 1, 10)
However, if the price or quantity is set to a negative number, no error will be reported:
item.price =-10
item.count =-1
invalid_item1 =Item('Bread',-1,10)
invalid_item2 =Item('Bread',1,-10)
From a grammatical point of view, these statements are legal, but from a business point of view, they are all illegal. So, how can we prevent this illegal assignment? One solution is to implement a Java style in accordance with the Java style In the setter method, set the price attribute through item.set_price(price), and then write the verification code in the set_price method. This is feasible, but not Pythonic enough. The Python style is to read and write through the attribute name:
print(item.price)
item.price = -10
The benefits of this have been mentioned before: the external interface will not be changed when the attribute is rewritten as a property. So, how to check the legality of -10 when item.price = -10? The most straightforward way is in _setattr Set interception in the _ method, but it is very troublesome, especially when there are many attributes that need to be verified. (If you don't believe it, you can refer to the second Python definition of read-only attributes to try).
The best solution provided by Python is through the setter method of the property:
classItem():
def __init__(self, category, count, price):
self.__cat = category # attribute
self.count = count # property
self.price = price # property
@ property
def cat(self):return self.__cat
@ property
def count(self):return self.__dict__['count']
@ count.setter
def count(self, value):if value <0:
raise ValueError('count can not be minus: %r'%(value))
self.__dict__['count']= value
@ property
def price(self):return self.__dict__['price']
@ price.setter
def price(self, value):if value <0:
raise ValueError('price can not be minus: %r'%(value))
self.__dict__['price']= value
Statements that were legal before can still run normally:
item =Item('Bread',1,10)
item.price =20
item.count =2print(item.price)
But when the following statement is executed, an error will be reported:
item =Item('Bread',1,-10)
# or
item.price =-10
Will report the same error:
---------------------------------------------------------------------------
ValueError Traceback(most recent call last)<ipython-input-93-4fcbd1284b2d in<module()----1 item.price =-10<ipython-input-91-7546240b5469 inprice(self, value)27 def price(self, value):28if value <0:---29 raise ValueError('price can not be minus: %r'%(value))30 self.__dict__['price']= value
ValueError: price can not be minus:-10
Other ways to define properties
@ Although the property in property can be used as a modifier, it is actually a class (please refer to the documentation for the specific API), so the above code can also be rewritten as:
classItem():
def __init__(self, category, count, price):
self.__cat = category # attribute
self.count = count # property
self.price = price # property
def get_cat(self):return self.__cat
def get_count(self):return self.__dict__['count']
def set_count(self, value):if value <0:
raise ValueError('count can not be minus: %r'%(value))
self.__dict__['count']= value
def get_price(self):return self.__dict__['price']
def set_price(self, value):if value <0:
raise ValueError('price can not be minus: %r'%(value))
self.__dict__['price']= value
bill =property(get_bill)
cat =property(get_cat)
count =property(get_count, set_count)
price =property(get_price, set_price)
The function meets the requirements, but the code itself looks very verbose, longer than the getter/setter style in Java. At this time, the code can be simplified through the property factory:
First define the property factory function that can be shared:
def readonly_prop(storage_name):
def getter(instance):return instance.__dict__[storage_name]returnproperty(getter)
def positive_mutable_prop(storage_name):
def getter(instance):return instance.__dict__[storage_name]
def setter(instance, value):if value <0:
raise ValueError('%s can not be minus: %r'%(storage_name, value))
instance.__dict__[storage_name]= value
returnproperty(getter, setter)
Then, the previous sample code can be simplified to:
classItem():
def __init__(self, category, count, price):
self.__cat = category # attribute
self.count = count # property
self.price = price # property
cat =readonly_prop('__cat')
count =positive_mutable_prop('count')
price =positive_mutable_prop('price')
In this way, access control and legality verification are achieved while ensuring the simplicity of the code.
property will not be overwritten by instance attribute
Previously, the attribute parsing process was shown in the article Attribute Access Process of Python Objects, from which it is known that the class attribute can be overridden by the instance attribute:
classFoo():
name ='Foo'
foo =Foo()
foo.name ='foo'
codes =['Foo.name','foo.name']for code in codes:print(code,'=',eval(code))
The output is:
Foo.name = Foo
foo.name = foo
But this kind of thing does not happen to properties:
classFoo():
@ property
def name(self):return'Foo'
foo =Foo()
foo.__dict__['name']='foo'#Can no longer pass foo.name assigned
codes =['Foo.name','foo.name']for code in codes:print(code,'=',eval(code))
Output:
Foo.name = <property object at 0x7fd135e7ecc8
foo.name = Foo
**At least two things can be seen: **
Accessing Foo.name through class Foo gets the property object, not the property value.
When foo.name is accessed, the property value of Foo.name is returned. The reason is that the priority of property is the highest in the property parsing process.
to sum up
**1. Python's attribute is different from property: **
attribute: data attribute + method
property: replace attribute with access control methods like getter/setter, for security reasons.
**2. Property can be defined in many ways: **
@ property
property(getter, setter)
property factory
3. Property has the highest priority in attribute parsing and will not be overwritten by instance attribute.
The above detailed explanation of Python from attribute to property is all the content shared by the editor. I hope to give you a reference.
Recommended Posts