Home > Article > Backend Development > Analysis of the usage skills of Python black magic @property decorator
@property What is the use? On the surface, it is to access a method as an attribute.
The code 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
You can see that although area is defined as a method, after adding @property, you can directly c.area and access it as a property.
Now the question comes, (not which excavator technology company Strong), 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 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
As you can see, 'evalute' is only output once. If you read my previous blog posts, you should have a good understanding of @lazy's mechanism.
Here, the lazy class has a __get__ method, indicating that it is a descriptor. When c.area was executed for the first time, due to order issues, it was first searched in c.__dict__. If it was not found, it was searched in the class space. In the class Circle, there is an area() method, so it was 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__.
Execute c again .area, go to c.__dict__ first, because it already exists at this time, so you will not go through the area() method and __get__.
Attention points
Please pay attention to the following code scenarios :
Code snippet 1:
Python2.6 code
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:
Python2.6 code
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 is that
class Parrot(object):
under python2.6, run the test respectively
snippet 1: An expected error message AttributeError: can't set attribute
Fragment 2: Correct operation
Refer to the python2.6 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 a running error. In the python2.6 document, 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.6, the built-in type object is not the default base class. If there is no clear explanation when defining the class (code snippet 2), the Parrot we define (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 it through the following methods
Python 2.6 code
class A: pass >>type(A) <type 'classobj'>
Python 2.6 code
class A(object): pass >>type(A) <type 'type'>
It can be seen from the returned 635a4e04c7149d4983c9db32b6c8ae2d, f5ae6bbdf402bfea04a71faa8c02de77 f5ae6bbdf402bfea04a71faa8c02de77 is the object type we need (Python 3.0 uses the object class as the default base class, so all will return f5ae6bbdf402bfea04a71faa8c02de77)
In order to consider the python version of the code Regarding compatibility issues during the transition period, 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.
For more Python black magic @property decorator usage skills analysis related articles, please pay attention to the PHP Chinese website!