Home  >  Article  >  Backend Development  >  Detailed explanation of dynamic properties and characteristics in Python_python

Detailed explanation of dynamic properties and characteristics in Python_python

不言
不言Original
2018-04-08 10:45:251411browse

This article mainly introduces the detailed explanation of dynamic properties and features in Python. The editor thinks it is quite good, so I will share it with you now and give it as a reference. Let’s follow the editor and take a look.

Introduction: This article records my key knowledge and personal experience in learning the basics of metaprogramming in Python. Friends who plan to get started with Python can come and learn and communicate together.

1. Use dynamic attributes to process JSON data sources

Attributes: In Python, data attributes and methods of processing data collectively called attributes.

Metaprogramming: Use metaclasses for programming, metaclass → class → object. Metaclasses are more abstract than classes and generate classes of classes.

1. Use dynamic attributes to access JSON class data

First edition: Use json.load(fp) to review data

from urllib.request import urlopen
import warnings
import os
import json

URL = 'http://www.oreilly.com/pub/sc/osconfeed'
JSON = 'data/osconfeed.json'

def load():
  if not os.path.exists(JSON):
    msg = 'downloading {} to {}'.format(URL, JSON)
    warnings.warn(msg) #如果需要下载就发出提醒。
    with urlopen(URL) as remote, open(JSON, 'wb') as local: #在with语句中使用两个上下文管理器分别用于读取和保存远程文件。
      local.write(remote.read())
  with open(JSON) as fp:
    return json.load(fp)#json.load函数解析JSON文件,返回Python原生对象。

Second edition: Use dynamic attributes to access JSON class data

The format of the first edition to query deep data is relatively lengthy, such as feed 'Schedule'40, we hope to The reading attributes are improved by methods such as feed.Schedule.events[40].name. And the second version of the class can be recursive and automatically handle nested maps and lists.

from collections import abc

class FronenJSON():
  def __init__(self,mapping):
    self.__data=dict(mapping)#创建副本,同时确保处理的是字典。
    
  def __getattr__(self, name):#仅当没有指定名称的属性才调用__getattr__方法。
    if hasattr(self,name):
      return getattr(self.__data,name)
    else:
      return FronenJSON.build(self.__data[name])
  
  @classmethod  
  def __build__(cls,obj):
    if isinstance(obj,abc.Mapping):#判断obj是否是映射。
      return cls(obj)#创建FrozenJSON对象。
    elif isinstance(obj,abc.MutableSequence):
      return [cls.build(item) for item in obj]#递归调用.build()方法,构建一个列表。
    else:#既不是字典也不是列表,则返回元素本身。
      return obj

Analysis: The key to the FronenJSON class is the __getattr__ method. The interpreter will call the special __getattr__ method only if the attribute cannot be obtained by conventional means (that is, the specified attribute cannot be found in the instance, class, or superclass).

2. Handling invalid attribute names

In Python, because keywords are reserved, attributes named keywords are invalid. Therefore, it is necessary to improve __init__ in the second version:

  def __init__(self,mapping):
    self.__data={}
    for key,value in mapping.items():
      if keyword.iskeyword(key):
        key+='_'#与Python关键字重复的key在尾部加上下划线。
      self.__data[key]=value

3. Use the special method __new__

Third Edition: Use the __new__ constructor to convert a class into a flexible object factory function.

from collections import abc

class FronenJSON():
  def __new__(cls, arg): # __new__是类方法,第一个参数是类本身cls。
    if isinstance(arg, abc.Mapping):
      return super().__new__(cls) #委托给超类object基类的__new__方法处理。
    elif isinstance(arg, abc.MutableSequence): # 余下方法与原先的build方法一致。
      return [cls(item) for item in arg]
    else:
      return arg
 
   def __init__(self,mapping):
    self.__data={}
    for key,value in mapping.items():
      if keyword.iskeyword(key):
        key+='_'
      self.__data[key]=value 

  def __getattr__(self, name):
    if hasattr(self,name):
      return getattr(self.__data,name)
    else:
      return FronenJSON(self.__data[name])

2. Characteristics

1. Class attributes , Instance attributes, private attributes and characteristics

Class attributes: Class attributes are initialized outside __init__() and belong to the class. All instances share one attribute.
Calling method: Class attributes are called internally using classname.class attribute name, and externally can be called using either classname.class attribute name or instancename.class attribute name.

Instance attributes: Instance attributes belong to each instance and do not interfere with each other.

Private attributes:

  1. Start with a single underscore _: It just tells others that this is a private attribute, and the outside world can still access and change it.

  2. Starts with double underscore __: It cannot be accessed or changed externally through instancename.propertyname. It is actually converted into _classname__propertyname.

Attribute: It is a class attribute used to manage instance attributes.
Feature usage: Often used to turn public properties into properties managed using read value methods and set value methods, and implement business rules without affecting the client code.

Note:

  1. Do not use the same name for instance properties and class properties. Otherwise, the instance attributes will cover the class attributes, causing hard-to-find errors.

  2. Instance attributes do not obscure class attributes, but class attributes obscure instance attributes.

This is because obj.attr does not look for attr starting from instance obj, but from obj.__class__; and only if there is no attribute named attr in the class, Python Only then will the attr be found in the instance.

In short, as far as the coverage level is concerned, class attributes > instance attributes > class attributes.

2. Use characteristics to verify attributes

Using characteristics can verify the validity of instance attributes, and at the same time, other attributes can be adjusted based on known attributes and relationships between attributes. , avoid hard coding.
Case: Suppose a store sells a variety of organic foods such as nuts and grains. Each customer's order will include a series of products in the store. We need to calculate the total price based on the customer's order.

Analysis: We do not want the weight of the product ordered by the customer to be a non-positive number. We need to use the @property decorator to obtain and set the value to verify the validity of the instance attributes. The code is as follows:

class LineItem():
  def __init__(self,description,weight,price):
    self.description=description
    self.weight=weight
    self.price=price

  def subtotal(self):
    return self.weight*self.price

  @property#读值。
  def weight(self):
    return self.__weight#真正的值存储在私有属性中。

  @weight.setter
  def weight(self,value):
    if value >0:
      self.__weight=value#有效值存入私有属性中。
    else:
      raise ValueError('Value must be > 0')#对于无效的值抛出ValueError。

Tips: When we need to set a read-only property, only use @property without using @func.setter.

Principle analysis: In order to better understand the principle of the @property decorator, we write a version of the code that has the same effect but does not use the decorator.

class LineItem:
  def __init__(self, description, weight, price):
    self.description = description
    self.weight = weight
    self.price = price

  def subtotal(self):
    return self.weight * self.price

  def get_weight(self): #普通读值方法。
    return self.__weight

  def set_weight(self, value): #普通设值方法。
    if value > 0:
      self.__weight = value
    else:
      raise ValueError('value must be > 0')
  weight = property(get_weight, set_weight) #构建property对象,赋值给公开的类特性。

Full signature of property constructor:

property(fget=None, fset=None, fdel=None, doc=None)

3, Feature factory function

There are two ways to abstractly define features, one is to use the feature factory function, and the other is to use a descriptor class.
Below we use the feature factory function to complete the order settlement case mentioned above:

def quantity(storage_name): 

  def qty_getter(instance): # instance指的是要把属性存储其中的LineItem实例。
    return instance.__dict__[storage_name] # 引用闭包中的自由变量storage_name,值直接从instance.__dict__中获取,以便跳过特性,防止无限递归。

  def qty_setter(instance, value): 
    if value > 0:
      instance.__dict__[storage_name] = value # 同理存储,跳过特性。
    else:
      raise ValueError('value must be > 0')

  return property(qty_getter, qty_setter) # 构建自定义特性对象并返回。

class LineItem:
  weight = quantity('weight') # 将自定义特性weight定义为类属性。
  price = quantity('price') # 同上。

  def __init__(self, description, weight, price):
    self.description = description
    self.weight = weight # 此处特性已经激活,可验证值的有效性。
    self.price = price

  def subtotal(self):
    return self.weight * self.price # 此处利用特性获取实例中存储的值。

4. Use features to delete attributes

class BlackKnight:
 def __init__(self):
   self.members = ['an arm', 'another arm',
           'a leg', 'another leg']
   self.phrases = ["'Tis but a scratch.",
           "It's just a flesh wound.",
           "I'm invincible!",
           "All right, we'll call it a draw."]

 @property
 def member(self):
   print('next member is:')
   return self.members[0]

 @member.deleter
 def member(self):
   text = 'BLACK KNIGHT (loses {})\n-- {}'
   print(text.format(self.members.pop(0), self.phrases.pop(0)))

To delete an attribute, just issue the command in the main program: del obj.attr

3. Important attributes and functions for processing attributes

1. Special attributes

  • __class__: A reference to the class to which the object belongs (that is, obj.__class__ has the same effect as type(obj)). Some special methods in Python, such as __getattr__, are only searched in the object's class, not in the instance.

  • __dict__: A map that stores writable attributes of an object or class.

  • __slots__: Classes can define this attribute to limit what attributes an instance has.

2. Built-in function

  • dir([object]): List most objects Attributes.

  • getattr(object,name[,default]): Get the attribute corresponding to the name string from the object object. The properties obtained may come from the class or superclass to which the object belongs.

  • hasattr(object,name): If the specified attribute exists in the object object, or the specified attribute can be obtained through the object object in some way (such as inheritance), return True.

  • setattr(object,name,value): Set the value of the specified attribute of the object object to value, provided that the object object can accept that value. This function may create a new property, or overwrite an existing property.

  • var([object]): Returns the __dict__ attribute of the object object.

3. Special method

  • __delattr__(self,name): Just use the del statement to delete the attribute , this method will be called.

  • __dir__(self): Called when the object is passed to the dir function, and the attributes are listed.

  • __getattr__(self,name): Called only when obtaining the specified attribute fails and after searching obj, Class and super class.

  • __getattribute__(self,name): This method is always called when trying to get the specified attribute. However, the exception is when the attribute being searched for is a special attribute or method. In order to prevent infinite recursion, the implementation of the __getattribute__ method should use super().__getattribute__(obj,name).

  • __setattr__(self,name,value): This method is always called when trying to set the specified attribute. The dot and setattr built-in functions

  • trigger this method.

Related recommendations:

Detailed explanation of the use of for loops in Python_python

Detailed explanation of python advanced functions with examples usage of


The above is the detailed content of Detailed explanation of dynamic properties and characteristics in Python_python. 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