Dieser Artikel stellt hauptsächlich die detaillierte Erklärung dynamischer Eigenschaften und Funktionen in Python vor. Der Herausgeber findet ihn recht gut, daher werde ich ihn jetzt mit Ihnen teilen und als Referenz verwenden. Kommen Sie und werfen Sie einen Blick mit dem Herausgeber
Einleitung: Dieser Artikel dokumentiert meine wichtigsten Kenntnisse und persönlichen Erfahrungen beim Erlernen der Grundlagen der Metaprogrammierung in Python. Freunde, die planen, mit Python zu beginnen, können vorbeikommen und gemeinsam lernen und kommunizieren.
Attribute: In Python Datenattribute und Methoden zur gemeinsamen Verarbeitung von Daten sogenannte Attribute.
Metaprogrammierung: Programmierung mit Metaklassen, Metaklasse → Klasse → Objekt. Metaklassen sind abstrakter als Klassen und erzeugen Klassen von Klassen.
1. Verwenden Sie dynamische Attribute, um auf JSON-Daten zuzugreifen
Erste Ausgabe: Verwenden Sie json.load(fp), um Daten zu überprüfen
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原生对象。
Zweite Ausgabe: Verwenden Sie dynamische Attribute, um auf JSON-Daten zuzugreifen
Das Format der ersten Ausgabe zum Anzeigen tiefer Daten ist relativ ausführlich, z. B. Feed „Zeitplan“. 40, Wir hoffen, dass Methoden wie „feed.Schedule.events[40].name“ verwendet werden können, um die Leseeigenschaften zu verbessern. Und die zweite Version der Klasse kann rekursiv sein und verschachtelte Karten und Listen automatisch verarbeiten.
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: Der Schlüssel zur FronenJSON-Klasse ist die Methode __getattr__. Der Interpreter ruft die spezielle Methode __getattr__ nur auf, wenn das Attribut nicht mit herkömmlichen Mitteln abgerufen werden kann (d. h. das angegebene Attribut kann in der Instanz, Klasse oder Oberklasse nicht gefunden werden).
2. Umgang mit ungültigen Attributnamen
Da in Python Schlüsselwörter reserviert sind, sind Attribute mit dem Namen Schlüsselwörter ungültig. Daher ist es notwendig, __init__ in der zweiten Version zu verbessern:
def __init__(self,mapping): self.__data={} for key,value in mapping.items(): if keyword.iskeyword(key): key+='_'#与Python关键字重复的key在尾部加上下划线。 self.__data[key]=value
3. Verwenden Sie die spezielle Methode __new__
Dritte Ausgabe: Verwenden Sie den Konstruktor __new__, um eine Klasse in eine flexible Objektfabrikfunktion umzuwandeln.
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])
1. Klassenattribute, Instanzattribute, private Attribute und Merkmale
Klassenattribute: Klassenattribute werden außerhalb von __init__() initialisiert und gehören zur Klasse. Alle Instanzen haben ein gemeinsames Attribut.
Aufrufmethode: Klassenattribute werden intern mit dem Attributnamen „classname.class“ aufgerufen und können extern entweder mit dem Attributnamen „classname.class“ oder dem Attributnamen „instancename.class“ aufgerufen werden.
Instanzattribute: Instanzattribute gehören zu jeder Instanz und beeinträchtigen sich nicht gegenseitig.
Private Attribute:
Beginnt mit einem einzelnen Unterstrich _: Es zeigt anderen nur an, dass es sich um ein privates Attribut handelt und auf externe Änderungen weiterhin zugegriffen werden kann.
Doppelter Unterstrich __ beginnt mit: Die Außenseite kann nicht über Instanzenname.Eigenschaftsname darauf zugreifen oder es ändern. Es wird tatsächlich in _Klassenname__Eigenschaftsname umgewandelt.
Attribut: Es handelt sich um ein Klassenattribut, das zum Verwalten von Instanzattributen verwendet wird.
Funktionsnutzung: Wird häufig verwendet, um öffentliche Eigenschaften in Eigenschaften umzuwandeln, die mithilfe von Lese- und Set-Methoden verwaltet werden, und um Geschäftsregeln zu implementieren, ohne den Clientcode zu beeinträchtigen.
Hinweis:
Verwenden Sie nicht denselben Namen für Instanzeigenschaften und Klasseneigenschaften. Andernfalls überdecken Instanzattribute Klassenattribute, was zu schwer zu findenden Fehlern führt.
Instanzattribute überschatten keine Klassenattribute, aber Klassenattribute überschatten Instanzattribute.
Das liegt daran, dass obj.attr nicht ausgehend von der Instanz obj nach attr sucht, sondern von obj.__class__, und zwar nur, wenn es in der Klasse kein Attribut mit dem Namen attr gibt, Python Nur dann Wird das Attribut in der Instanz gefunden?
Kurz gesagt, was die Schattenebene betrifft, sind Klasseneigenschaften > Instanzeigenschaften > Klasseneigenschaften.
2. Verwenden Sie Merkmale, um Attribute zu überprüfen.
Durch die Verwendung von Merkmalen können Sie die Gültigkeit von Instanzattributen überprüfen und andere Attribute basierend auf bekannten Attributen und Beziehungen zwischen Attributen anpassen Codierung.
Angenommen, ein Geschäft verkauft eine Vielzahl von Bio-Lebensmitteln wie Nüssen und Getreide. Die Bestellung jedes Kunden umfasst eine Reihe von Produkten im Geschäft. Wir müssen den Gesamtpreis auf der Grundlage der Bestellung des Kunden berechnen.
Analyse: Wir möchten nicht, dass das Gewicht des vom Kunden bestellten Produkts eine nicht positive Zahl ist. Wir müssen den @property-Dekorator verwenden, um den Wert zu erhalten und festzulegen, um die Gültigkeit zu überprüfen der Instanzattribute. Der Code lautet wie folgt:
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。
Tipps: Wenn wir eine schreibgeschützte Eigenschaft festlegen müssen, verwenden Sie nur @property, ohne @func.setter .
Prinzipanalyse: Um das Prinzip des @property-Dekorators besser zu verstehen, schreiben wir eine Version des Codes, die den gleichen Effekt hat, aber den Dekorator nicht verwendet.
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对象,赋值给公开的类特性。
Vollständige Unterschrift des Grundstückserbauers:
property(fget=None, fset=None, fdel=None, doc=None)
3. Feature-Factory-Funktion
Es gibt zwei Möglichkeiten, Features abstrakt zu definieren: Eine besteht darin, die Feature-Factory-Funktion zu verwenden, und die andere darin, die Deskriptorklasse zu verwenden.
Im Folgenden verwenden wir die Feature-Factory-Funktion, um den oben genannten Fall der Auftragsabwicklung abzuschließen:
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. Verwenden Sie Features, um Attribute zu löschen
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)))
Um ein Attribut zu löschen, geben Sie einfach den Befehl im Hauptprogramm ein: del obj.attr
1 🎜>
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der dynamischen Eigenschaften und Merkmale in Python_python. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!