Python黒魔法@property装飾器の使用テクニック解析

4669 ワード

@propertyは何の役に立ちますか?表面的には、一つの方法を属性でアクセスすることである.上のコード、コードが一番はっきりしています.

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 


areaはメソッドとして定義形式であるが,@propertyを加えると,直接c.areaとして属性としてアクセスできることがわかる.今問題が来て、(掘削機の技術のどの家が強いのではありません)、c.areaを呼び出すたびに、一度計算して、cpuを浪費して、どのようにして一度だけ計算することができますか?これが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 

「evalute」は一度しか出力されていないことがわかります.私の前のいくつかのブログを見たら、@lazyのメカニズムはよく理解できるはずです.ここでlazyクラスには_get__方法、説明はディスクリプタで、初めてc.areaを実行するとき、順序の問題で、先にc._dict__の中で探して、探し当てていないで、類の空間に行って探して、類のCircleの中で、area()方法があって、そこで_get__阻止するで_get__では、インスタンスのarea()メソッドを呼び出して結果を算出し、インスタンスに同じ名前のプロパティを動的に追加して結果を割り当てます.すなわち、c._dict__中去c.areaを再度実行する場合は、先にc._dict__探して、この時すでにあったため、area()の方法と__を通りませんget__はい.
注意:次のコードシーンに注意してください.
コードクリップ1:Python 2.6コード

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 

コードクリップ2:Python 2.6コード

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 

コード1、2の違いは

class Parrot(object): 

python 2.6で、テストフラグメント1を別々に実行します.予想されるエラーメッセージが表示されます.AttributeError:can't set attributeフラグメント2:正しく実行されます.
参照python 2.6ドキュメント,@propertyはready-only propertyを提供し,以上のコードは対応する@voltageを提供しない.setter、クリップ2のコードはpython 2でエラーを実行するように要求されます.6ドキュメントでは、次の情報を参照できます.
BIF: property([fget[, fset[, fdel[, doc]]]]) Return a property attribute for new-style classes (classes that derive from object). python 2にいたのか.6では、組み込み型objectはデフォルトのベースクラスではありません.クラスを定義するときに、明確な説明がなければ(コードクリップ2)、私たちが定義したParrot(コードクリップ2)はobjectを継承しません.
objectクラスは、ドキュメントで次の情報を調べるために必要な@property機能を提供しています.
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__().
Python 2.6コードも以下の方法で検証できます

class A: 
  pass 

>>type(A) 
 


Python 2.6コード

class A(object): 
  pass 

>>type(A) 
 


返されるのは、私たちが必要とするobjectタイプであることがわかります(python 3.0はobjectクラスをデフォルトのベースクラスとしているので、すべて返されます).
コードのpythonバージョンの移行期間の互換性の問題を考慮するために、classファイルを定義すべき時、objectを明示的に定義すべきで、良い習慣だと思います.
最後のコードは次のとおりです.

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 


また,@propertyは2.6,3.0に追加され,2.5にはこの機能はない.