@property decorator can turn a method into a property and call it. Let’s take a look at the Python black magic @property decorator usage skills analysis
What is the use of @property? On the surface It seems that a method is accessed as an attribute.
The code above is the clearest.
class Circle(object): def __init__(self, radius): self.radius = radius @property def area(self): return 3.14 * self.radius ** 2 c = Circle(4) print c.radius print c.area
It can be seen that although area is defined as a method, After adding @property, you can directly access c.area as a property.
Now the question is, every time c.area is called, it will be calculated once, which is a waste of CPU. How can it be calculated only once? This is the lazy property.
class lazy(object): def __init__(self, func): self.func = func def __get__(self, instance, cls): val = self.func(instance) setattr(instance, self.func.__name__, val) return val class Circle(object): def __init__(self, radius): self.radius = radius @lazy def area(self): print 'evalute' return 3.14 * self.radius ** 2 c = Circle(4) print c.radius print c.area print c.area print c.area
You can see that 'evalute' is only output once, and you should have a good understanding of @lazy's mechanism.
Here, the lazy class has the __get__ method , indicating that it is a descriptor. When c.area is executed for the first time, due to order issues, it is first searched in c.__dict__. If it is not found, it is searched in the class space. In the class Circle, there is an area() method, so It is intercepted by __get__.
In __get__, call the area() method of the instance to calculate the result, and dynamically add an attribute with the same name to the instance and assign the result to it, that is, add it to c.__dict__ Go to it.
When executing c.area again, first go to c.__dict__ to find it, because it already exists at this time, so you will not go through the area() method and __get__.
Attention
Please pay attention to the following code scenarios:
Code snippet 1:
class Parrot(object): def __init__(self): self._voltage = 100000 @property def voltage(self): """Get the current voltage.""" return self._voltage if __name__ == "__main__": # instance p = Parrot() # similarly invoke "getter" via @property print p.voltage # update, similarly invoke "setter" p.voltage = 12
Code snippet 2:
class Parrot: def __init__(self): self._voltage = 100000 @property def voltage(self): """Get the current voltage.""" return self._voltage if __name__ == "__main__": # instance p = Parrot() # similarly invoke "getter" via @property print p.voltage # update, similarly invoke "setter" p.voltage = 12
The difference between codes 1 and 2 It depends on
class Parrot(object):
Under python2, run the test separately
Fragment 1: An expected error message AttributeError: can't set attribute will be prompted
Fragment 2: Correct operation
Refer to the python2 document, @property will provide a ready-only property, the above code does not provide the corresponding @voltage.setter, it stands to reason that the code in fragment 2 will prompt to run Error, in the python2 documentation, we can find the following information:
BIF:
property([fget[, fset[, fdel[, doc]]]])
Return a property attribute for new-style classes (classes that derive from object).
It turns out that under python2, the built-in type object is not the default base class, if it is not explicitly stated when defining the class ( Code snippet 2), the Parrot we defined (code snippet 2) will not inherit object
and the object class just provides the @property function we need. We can find the following information in the document:
new-style class
Any class which inherits from object. This includes all built-in types like list and dict. Only new-style classes can use Python's newer, versatile features like __slots__, descriptors, properties, and __getattribute__().
At the same time, we can also verify through the following methods
class A: pass >>type(A) <type 'classobj'>
class A(object): pass >>type(A) <type 'type'>
From the returned
In order to consider the code Regarding the compatibility issues during the transition period of python versions, I think that when defining class files, object should be explicitly defined as a good habit.
The final code will be as follows:
class Parrot(object): def __init__(self): self._voltage = 100000 @property def voltage(self): """Get the current voltage.""" return self._voltage @voltage.setter def voltage(self, new_value): self._voltage = new_value if __name__ == "__main__": # instance p = Parrot() # similarly invoke "getter" via @property print p.voltage # update, similarly invoke "setter" p.voltage = 12
In addition , @property was added in 2.6 and 3.0, and 2.5 does not have this function.
The above is the detailed content of Detailed explanation of property usage of Python decorator. For more information, please follow other related articles on the PHP Chinese website!