Home  >  Article  >  Backend Development  >  Detailed explanation of property usage of Python decorator

Detailed explanation of property usage of Python decorator

巴扎黑
巴扎黑Original
2017-08-18 17:11:532121browse

@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) 
class A(object): 
  pass 
>>type(A) 

From the returned 635a4e04c7149d4983c9db32b6c8ae2d, f5ae6bbdf402bfea04a71faa8c02de77 can It can be seen that 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 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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn