Maison  >  Article  >  développement back-end  >  Explication détaillée des propriétés et caractéristiques dynamiques dans Python_python

Explication détaillée des propriétés et caractéristiques dynamiques dans Python_python

不言
不言original
2018-04-08 10:45:251415parcourir

Cet article présente principalement l'explication détaillée des propriétés et fonctionnalités dynamiques de Python. L'éditeur pense qu'il est assez bon, je vais donc le partager avec vous maintenant et le donner comme référence. Venez jeter un œil avec l'éditeur

Introduction : Cet article enregistre mes connaissances clés et mes expériences personnelles dans l'apprentissage des bases de la métaprogrammation en Python. Les amis qui envisagent de se lancer avec Python peuvent venir apprendre et communiquer ensemble.

1. Utiliser des attributs dynamiques pour traiter les sources de données JSON

Attributs : en Python, les attributs de données et les méthodes de traitement des données collectivement. appelés attributs.

Métaprogrammation : Programmation avec des métaclasses, métaclasse → classe → objet Les métaclasses sont plus abstraites que les classes et génèrent des classes de classes.

1. Utilisez des attributs dynamiques pour accéder aux données JSON

Première édition : utilisez json.load(fp) pour examiner les données

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原生对象。

Deuxième édition : Utilisez des attributs dynamiques pour accéder aux données JSON

Le format de la première édition pour afficher les données approfondies est relativement détaillé, comme le flux « Planification » 40, nous espérons que des méthodes telles que feed.Schedule.events[40].name pourront être utilisées pour améliorer les propriétés de lecture. Et la deuxième version de la classe peut être récursive et gérer automatiquement les cartes et listes imbriquées.

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

Analyse : La clé de la classe FronenJSON est la méthode __getattr__. L'interpréteur appellera la méthode spéciale __getattr__ uniquement si l'attribut ne peut pas être obtenu par des moyens conventionnels (c'est-à-dire que l'attribut spécifié est introuvable dans l'instance, la classe ou la superclasse).

2. Gestion des noms d'attributs non valides

En Python, les mots-clés étant réservés, les attributs nommés mots-clés ne sont pas valides. Il est donc nécessaire d'améliorer __init__ dans la deuxième 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. Utilisez la méthode spéciale __new__

Troisième édition : utilisez le constructeur __new__ pour convertir une classe en fonction de fabrique d'objets flexible.

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. Caractéristiques

1. Attributs de classe, attributs d'instance, attributs privés et caractéristiques

Attributs de classe : les attributs de classe sont initialisés en dehors de __init__() et appartiennent à la classe.
Méthode d'appel : les attributs de classe sont appelés en interne à l'aide du nom d'attribut classname.class, et en externe peuvent être appelés en utilisant soit le nom d'attribut classname.class, soit le nom d'attribut instancename.class.

Attributs d'instance : les attributs d'instance appartiennent à chaque instance et n'interfèrent pas les uns avec les autres.

Attributs privés :

  1. Commençant par un seul trait de soulignement _ : il indique simplement aux autres qu'il s'agit d'un attribut privé et que le monde extérieur peut toujours y accéder et le modifier.

  2. Commençant par un double trait de soulignement __ : l'accès externe ou la modification ne peuvent pas être effectués via instancename.propertyname. Il est en fait converti en _classname__propertyname.

Attribut : C'est un attribut de classe utilisé pour gérer les attributs d'instance.
Utilisation des fonctionnalités : souvent utilisée pour transformer des propriétés publiques en propriétés gérées à l'aide de méthodes de lecture et de définition, et pour implémenter des règles métier sans affecter le code client.

Remarque :

  1. N'utilisez pas le même nom pour les propriétés d'instance et les propriétés de classe. Sinon, les attributs d'instance couvriront les attributs de classe, provoquant des erreurs difficiles à trouver.

  2. Les attributs d'instance n'éclipsent pas les attributs de classe, mais les attributs de classe éclipsent les attributs d'instance.

C'est parce que obj.attr ne recherche pas attr à partir de l'instance obj, mais à partir de obj.__class__ et seulement s'il n'y a pas d'attribut nommé attr dans la classe, Python Seulement alors ; l'attr sera-t-il trouvé dans l'instance.

En bref, en ce qui concerne le niveau d'ombre, propriétés de classe > propriétés d'instance > propriétés de classe.

2. Utilisez les caractéristiques pour vérifier les attributs

L'utilisation des caractéristiques peut vérifier la validité des attributs d'instance et ajuster d'autres attributs en fonction des attributs connus et des relations entre les attributs, éviter le codage en dur. .
Cas : Supposons qu'un magasin vende une variété d'aliments biologiques tels que des noix et des céréales. La commande de chaque client comprendra une série de produits dans le magasin. Nous devons calculer le prix total en fonction de la commande du client.

Analyse : Nous ne voulons pas que le poids du produit commandé par le client soit un nombre non positif. Nous devons utiliser le décorateur @property pour obtenir et définir la valeur afin d'en vérifier la validité. des attributs de l'instance. Le code est le suivant :

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。

Conseils : Lorsque nous devons définir une propriété en lecture seule, utilisez uniquement @property sans utiliser @func.setter .

Analyse du principe : Afin de mieux comprendre le principe du décorateur @property, nous écrivons une version du code qui a le même effet mais n'utilise pas le décorateur.

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对象,赋值给公开的类特性。

Signature complète du constructeur immobilier :

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

3. Fonction d'usine de fonctionnalités

Il existe deux façons de définir de manière abstraite des fonctionnalités, l'une consiste à utiliser la fonction d'usine de fonctionnalités et l'autre consiste à utiliser la classe de descripteur.
Ci-dessous, nous utilisons la fonction Feature Factory pour compléter le cas de règlement de commande mentionné ci-dessus :

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. Utilisez les fonctionnalités pour supprimer des attributs

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)))

Pour supprimer un attribut, lancez simplement la commande dans le programme principal : del obj.attr

3. Attributs et fonctions importants pour le traitement des attributs

1. 🎜>

    __class__ : une référence à la classe à laquelle appartient l'objet (c'est-à-dire que obj.__class__ a le même effet que type(obj)). Certaines méthodes spéciales en Python, telles que __getattr__, sont recherchées uniquement dans la classe de l'objet, pas dans l'instance.
  • __dict__ : Une carte qui stocke les attributs inscriptibles d'un objet ou d'une classe.
  • __slots__ : les classes peuvent définir cet attribut pour limiter les attributs d'une instance.

2. Fonction intégrée

    dir([object]) : répertorie la propriété de la plupart des objets. .
  • getattr(object,name[,default]) : récupère l'attribut correspondant à la chaîne de nom de l'objet objet. Les propriétés obtenues peuvent provenir de la classe ou superclasse à laquelle appartient l'objet.
  • hasattr(object,name): Si l'attribut spécifié existe dans l'objet objet, ou si l'attribut spécifié peut être obtenu via l'objet objet d'une manière ou d'une autre (comme l'héritage), retournez Vrai.
  • setattr(object,name,value) : Définissez la valeur de l'attribut spécifié de l'objet objet sur valeur, à condition que l'objet objet puisse accepter cette valeur. Cette fonction peut créer une nouvelle propriété ou écraser une propriété existante.
  • var([object]) : renvoie l'attribut __dict__ de l'objet objet.

3. Méthodes spéciales

    __delattr__(self,name) : utilisez simplement l'instruction del pour delete attributs , cette méthode sera appelée.
  • __dir__(self) : Appelé lorsque l'objet est passé à la fonction dir et répertorie les attributs.
  • __getattr__(self,name) : appelé uniquement en cas d'échec de l'obtention de l'attribut spécifié et après avoir recherché obj, Class et super class.
  • __getattribute__(self,name) : Cette méthode est toujours appelée lorsque vous essayez d'obtenir l'attribut spécifié. Toutefois, l'exception concerne les cas où l'attribut recherché est un attribut ou une méthode spéciale. Afin d'éviter une récursion infinie, l'implémentation de la méthode __getattribute__ doit utiliser super().__getattribute__(obj,name).
  • __setattr__(self,name,value) : Cette méthode est toujours appelée lorsque vous essayez de définir l'attribut spécifié. Les fonctions intégrées dot et setattr
  • déclenchent cette méthode.
  • Recommandations associées :

Explication détaillée de l'utilisation de la boucle for dans Python_python

Explication détaillée de python fonctions avancées avec exemples Utilisation de


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn