Detailed explanation of property usage of Python decorator

巴扎黑
Release: 2017-08-18 17:11:53
Original
2276 people have browsed it

@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
Copy after login

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
Copy after login

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
Copy after login

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
Copy after login

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 &#39;classobj&#39;>
Copy after login
class A(object): 
  pass 
>>type(A) 
<type &#39;type&#39;>
Copy after login

From the returned , can It can be seen that is the object type we need (python 3.0 uses the object class as the default base class, so all will return )

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
Copy after login

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!

source:php.cn
Statement of this Website
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
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template