この記事はPythonの動的プロパティと機能の詳細な説明を主に紹介していますが、編集者は非常に優れていると思いますので、参考として共有します。エディターと一緒に見てみましょう
はじめに: この記事は、Python でのメタプログラミングの基礎を学ぶ際の私の重要な知識と個人的な経験を記録しています。これから Python を始めようと考えている友人は、一緒に学び、コミュニケーションをとることができます。
属性: Python では、データ属性とデータ処理メソッドを総称して属性と呼びます。
メタプログラミング: プログラミングにはメタクラスを使用します。メタクラス→クラス→オブジェクト。メタクラスはクラスよりも抽象的であり、クラスのクラスを生成します。
1. 動的プロパティを使用して JSON クラス データにアクセスする
第 1 版: json.load(fp) を使用してデータを確認する
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原生对象。
第 2 版: 動的プロパティを使用して JSON クラス データにアクセスする
第 2 版 最初のバージョンのディープ データをクエリするための形式は、feed'Schedule'40 など比較的長かったですが、属性の読み取りに feed.Schedule.events[40].name を使用することで改善したいと考えています。また、クラスの 2 番目のバージョンは再帰的で、ネストされたマップとリストを自動的に処理できます。
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
分析: FronenJSON クラスのキーは __getattr__ メソッドです。インタプリタは、従来の手段を使用して属性を取得できない場合 (つまり、指定された属性がインスタンス、クラス、またはスーパークラスで見つからない場合) にのみ、特別な __getattr__ メソッドを呼び出します。
2. 無効な属性名の処理
Python ではキーワードが予約されているため、キーワードという名前の属性は無効です。したがって、第 2 版では __init__ を改善する必要があります:
def __init__(self,mapping): self.__data={} for key,value in mapping.items(): if keyword.iskeyword(key): key+='_'#与Python关键字重复的key在尾部加上下划线。 self.__data[key]=value
3. 特別なメソッド __new__
を使用します。 第 3 版: __new__ コンストラクターを使用して、クラスを柔軟な 1 つのオブジェクト ファクトリ関数に変換します。
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. クラス属性、インスタンス属性、プライベート属性と特性
クラス属性: クラス属性は __init__() の外部で初期化され、クラスに属します。すべてのインスタンスはプロパティを共有します。
呼び出し方法: クラス属性は、classname.class 属性名を使用して内部的に呼び出され、classname.class 属性名またはinstancename.class 属性名を使用して外部的に呼び出すことができます。
インスタンス属性: インスタンス属性は各インスタンスに属し、相互に干渉しません。
プライベート属性:
単一のアンダースコアで始まる _: これは、これがプライベート属性であることを他の人に伝えるだけで、外部の世界は引き続きアクセスして変更できます。
二重アンダースコア __ で始まる: インスタンス名.プロパティ名を通じて外部アクセスまたは変更を行うことはできません。実際には、_クラス名__プロパティ名に変換されます。
特徴: インスタンスの属性を管理するために使用されるクラス属性です。
機能の使用法: パブリック プロパティを、値の読み取りメソッドと値の設定メソッドを使用して管理されるプロパティに変換し、クライアント コードに影響を与えずにビジネス ルールを実装するためによく使用されます。
注:
インスタンスのプロパティとクラスのプロパティに同じ名前を使用しないでください。そうしないと、インスタンス属性がクラス属性をカバーしてしまい、見つけにくいエラーが発生します。
インスタンス属性はクラス属性を曖昧にしませんが、クラス属性はインスタンス属性を曖昧にします。
これは、obj.attr はインスタンス obj からではなく、obj.__class__; から attr の検索を開始し、クラスに attr という名前の属性がない場合にのみ、Python がインスタンス内の attr を検索するためです。
つまり、カバレッジレベルで言えば、クラス属性 > インスタンス属性 > クラス属性です。
2. 特性を使用して属性を検証する
特性を使用すると、インスタンス属性の有効性を検証でき、同時に、ハードコーディングを回避するために、既知の属性と属性間の関係に基づいて他の属性を調整できます。
ケース: 店舗がナッツや穀物などのさまざまなオーガニック食品を販売しているとします。各顧客の注文には、店内の一連の製品が含まれます。顧客の注文に基づいて合計価格を計算する必要があります。
分析: 顧客の注文の製品重量が正でない数値になることは望ましくありません。インスタンス属性の有効性を検証するために、@property デコレーターを使用して値を取得および設定する必要があります。コードは次のとおりです:
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。
ヒント: 読み取り専用プロパティを設定する必要がある場合は、@func.setter を使用せずに @property のみを使用します。
原理分析: @property デコレーターの原理をより深く理解するために、同じ効果を持つがデコレーターを使用しないバージョンのコードを作成します。
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对象,赋值给公开的类特性。
プロパティ コンストラクターメソッドの完全な署名:
property(fget=None, fset=None, fdel=None, doc=None)
3. 特徴ファクトリ関数
特徴を抽象的に定義するには 2 つの方法があります。1 つは、特徴ファクトリ関数を使用することです。もう 1 つは、説明 Symbol クラスを使用することです。
以下では、フィーチャーファクトリー関数を使用して、上記の注文決済ケースを完了します:
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. フィーチャーを使用して属性を削除します
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)))
属性を削除するには、メインプログラム:del obj.attr
1. 特別な属性
__class__: オブジェクトが属するクラスへの参照 (つまり、obj.__class__ と type(obj) )同じ効果があります)。 __getattr__ などの Python の一部の特別なメソッドは、インスタンス内ではなく、オブジェクトのクラス内でのみ検索されます。
__dict__: オブジェクトまたはクラスの書き込み可能な属性を格納するマップ。
__slots__: クラスはこの属性を定義して、インスタンスが持つ属性を制限できます。
2. 組み込み関数
dir([object]): オブジェクトのほとんどのプロパティをリストします。
getattr(object,name[,default]): オブジェクトオブジェクトから名前文字列に対応する属性を取得します。取得されるプロパティは、オブジェクトが属するクラスまたはスーパークラスから取得される場合があります。
hasattr(object,name): 指定された属性がオブジェクト object に存在する場合、または指定された属性が何らかの方法 (継承など) でオブジェクト オブジェクトを通じて取得できる場合は、True を返します。
setattr(object,name,value): オブジェクト object がその値を受け入れることができる場合、オブジェクト object の指定された属性の値を value に設定します。この関数は、新しいプロパティを作成することも、既存のプロパティを上書きすることもできます。
var([object]): object オブジェクトの __dict__ 属性を返します。
3. 特別なメソッド
__delattr__(self,name): このメソッドは、del ステートメントが属性を削除するために使用されるたびに呼び出されます。
__dir__(self): オブジェクトが dir 関数に渡され、属性をリストするときに呼び出されます。
__getattr__(self,name): 指定された属性の取得が失敗した場合と、obj、Class、およびスーパークラスの検索後にのみ呼び出されます。
__getattribute__(self,name): このメソッドは、指定された属性を取得しようとするときに常に呼び出されます。ただし、検索対象の属性が特殊な属性またはメソッドである場合は例外です。無限再帰を防ぐために、__getattribute__ メソッドの実装では super().__getattribute__(obj,name) を使用する必要があります。
__setattr__(self,name,value): このメソッドは、指定された属性を設定しようとするときに常に呼び出されます。ドット記号と setattr 組み込み関数
がこのメソッドをトリガーします。
関連する推奨事項:
Python_python での for ループの使用方法の詳細な説明
以上がPython_pythonの動的プロパティと特性の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。