組み込み関数property
がどのように機能するのかを理解したいのですが。私を混乱させているのは、property
はデコレータとしても使えるということですが、組み込み関数として使われるときだけ引数をとり、デコレータとして使われるときではありません。
この例は ドキュメント :からのものです。
class C(object):
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
property
の引数は、getx
、setx
、delx
、およびdoc文字列です。
以下のコードではproperty
がデコレータとして使われています。その目的はx
関数ですが、上記のコードでは引数に目的関数の場所はありません。
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
そして、x.setter
とx.deleter
デコレータはどのように作成されますか?私は混乱しています。
property()
関数は特別な 記述子オブジェクト を返します。
>>> property()
<property object at 0x10ff07940>
extra メソッドを持つのはこのオブジェクトです。
>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>
これらはデコレータとして機能します too 。それらは新しいプロパティオブジェクトを返します。
>>> property().getter(None)
<property object at 0x10ff079f0>
これは古いオブジェクトのコピーですが、関数の1つが置き換えられています。
@decorator
構文は単なる構文上の糖であることを忘れないでください。構文は次のとおりです。
@property
def foo(self): return self._foo
本当に同じことを意味する
def foo(self): return self._foo
foo = property(foo)
それでfoo
関数はproperty(foo)
に置き換えられます。上で見たものは特別なオブジェクトです。それであなたが@foo.setter()
を使うとき、あなたがしているのは私が上であなたに示したproperty().setter
メソッドを呼ぶことです、そしてそれはプロパティの新しいコピーを返します、しかし今回はsetter関数で装飾されたメソッドで置き換えられます。
次のシーケンスでも、これらのデコレータメソッドを使用してフルオンプロパティを作成します。
まず、ゲッターを1つだけ使って関数とproperty
オブジェクトを作成します。
>>> def getter(self): print 'Get!'
...
>>> def setter(self, value): print 'Set to {!r}!'.format(value)
...
>>> def deleter(self): print 'Delete!'
...
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True
次に.setter()
メソッドを使ってセッターを追加します。
>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True
最後に.deleter()
メソッドを使ってデリミタを追加します。
>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True
最後になりましたが、property
オブジェクトは 記述子オブジェクト として機能するため、インスタンス属性の取得、設定、削除に使用する .__get__()
、 .__set__()
、および .__delete__()
のメソッドがあります。
>>> class Foo(object): pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!
Descriptor Howtoは 純粋なpythonサンプル実装property()
型のものを含みます。
class Property(object): "Emulate PyProperty_Type() in Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel if doc is None and fget is not None: doc = fget.__doc__ self.__doc__ = doc def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") return self.fget(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError("can't set attribute") self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError("can't delete attribute") self.fdel(obj) def getter(self, fget): return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.__doc__)
ドキュメントによると これは読み取り専用のプロパティを作成するためのショートカットにすぎません。そう
@property
def x(self):
return self._x
と同等です
def getx(self):
return self._x
x = property(getx)
最初の部分は簡単です:
@property
def x(self): ...
と同じです
def x(self): ...
x = property(x)
property
を作成するための単純化された構文です。次のステップは、このプロパティをsetterとdeleterで拡張することです。そしてこれは適切な方法で起こります:
@x.setter
def x(self, value): ...
古いx
と指定されたセッターのすべてを継承する新しいプロパティを返します。
x.deleter
は同じように機能します。
以下は@property
を実装する方法の最小限の例です。
class Thing:
def __init__(self, my_Word):
self._Word = my_Word
@property
def Word(self):
return self._Word
>>> print( Thing('ok').Word )
'ok'
そうでなければWord
はプロパティではなくメソッドのままです。
class Thing:
def __init__(self, my_Word):
self._Word = my_Word
def Word(self):
return self._Word
>>> print( Thing('ok').Word() )
'ok'
これは次のとおりです。
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
と同じです:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
def _x_set(self, value):
self._x = value
def _x_del(self):
del self._x
x = property(_x_get, _x_set, _x_del,
"I'm the 'x' property.")
と同じです:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
def _x_set(self, value):
self._x = value
def _x_del(self):
del self._x
x = property(_x_get, doc="I'm the 'x' property.")
x = x.setter(_x_set)
x = x.deleter(_x_del)
と同じです:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
x = property(_x_get, doc="I'm the 'x' property.")
def _x_set(self, value):
self._x = value
x = x.setter(_x_set)
def _x_del(self):
del self._x
x = x.deleter(_x_del)
これは以下と同じです。
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
以下は、 here から取られたコードをリファクタリングしなければならないときの@property
の助けとなる別の例です(私はそれを以下に要約します)
このようなクラスMoney
を作成したと想像してください。
class Money:
def __init__(self, dollars, cents):
self.dollars = dollars
self.cents = cents
そしてユーザは自分が使用するこのクラスに応じてライブラリを作成します。
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.
Money
クラスを変更してdollars
属性とcents
属性を取り除くことにしましたが、セントの合計金額のみを追跡することにしました。
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
上記のユーザが今までと同じように自分のライブラリを実行しようとした場合
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
エラーになります
AttributeError: 'Money'オブジェクトには 'ドル'属性はありません
つまり、元々のMoney
クラスに頼っている人全員が、dollars
とcents
が使用されているすべてのコード行を変更する必要があるということです。 @property
を使って!
こうやって:
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
# Getter and setter for dollars...
@property
def dollars(self):
return self.total_cents // 100
@dollars.setter
def dollars(self, new_dollars):
self.total_cents = 100 * new_dollars + self.cents
# And the getter and setter for cents.
@property
def cents(self):
return self.total_cents % 100
@cents.setter
def cents(self, new_cents):
self.total_cents = 100 * self.dollars + new_cents
私達が今私達の図書館から呼ぶとき
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.
それは予想通りに動作するでしょう、そして私たちは私たちのライブラリのコードの一行を変更する必要はありませんでした!実際、依存しているライブラリが変更されたことを知る必要さえありません。
setter
もうまく動作します。
money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.
money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.
ここですべての記事を読み、実際の例が必要な場合があることに気付きました。なぜ、実際には@propertyがあるのですか。したがって、認証システムを使用するFlaskアプリを検討してください。 モデルのユーザーをmodels.py
で宣言します。
class User(UserMixin, db.Model):
__table= 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
...
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
このコードでは、直接アクセスしようとするとpassword
アサーションをトリガーする@property
を使用して属性AttributeError
を「隠し」、実際のインスタンス変数password_hash
を設定するために@ property.setterを使用しました。
これでauth/views.py
でUserをインスタンス化することができます。
...
@auth.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
if form.validate_on_submit():
user = User(email=form.email.data,
username=form.username.data,
password=form.password.data)
db.session.add(user)
db.session.commit()
...
ユーザーがフォームに記入するときに登録フォームから来る属性password
に注意してください。パスワードの確認はフロントエンドでEqualTo('password', message='Passwords must match')
で行われます(あなたが疑問に思っている場合は、それはFlaskフォームに関連する別のトピックです)。
この例が役に立つことを願っています
Pythonデコレータから始めましょう。
Pythonデコレータは、既に定義されている関数にいくつかの追加機能を追加するのに役立つ関数です。
Pythonでは、すべてがオブジェクトです。Pythonでは、すべてがオブジェクトです。 Pythonの関数はファーストクラスのオブジェクトなので、変数で参照したり、リストに追加したり、他の関数に引数として渡したりすることができます。
次のコードスニペットを考えてください。
def decorator_func(fun):
def wrapper_func():
print("Wrapper function started")
fun()
print("Given function decorated")
# Wrapper function add something to the passed function and decorator
# returns the wrapper function
return wrapper_func
def say_bye():
print("bye!!")
say_bye = decorator_func(say_bye)
say_bye()
# Output:
# Wrapper function started
# bye
# Given function decorated
ここで、デコレータ関数がsay_hello関数を修正し、そこにいくつかのコード行を追加したと言えます。
デコレータのPython構文
def decorator_func(fun):
def wrapper_func():
print("Wrapper function started")
fun()
print("Given function decorated")
# Wrapper function add something to the passed function and decorator
# returns the wrapper function
return wrapper_func
@decorator_func
def say_bye():
print("bye!!")
say_bye()
ケース・シナリオよりもすべてをまとめてみましょうが、その前におっとの原則について話しましょう。
ゲッターおよびセッターは、データのカプセル化の原則を保証するために、多くのオブジェクト指向プログラミング言語で使用されています(データをこれらのデータを操作するメソッドとバンドルするものと見なされています)。
これらのメソッドは、もちろんデータを取得するためのゲッターとデータを変更するためのセッターです。
この原則によれば、クラスの属性は他のコードからそれらを隠し保護するために非公開にされます。
うん、 @property は基本的にゲッターとセッターを使うPythonicの方法です。
Pythonはpropertyと呼ばれる素晴らしい概念を持っています。これはオブジェクト指向プログラマーの生活をもっと簡単にします。
あなたが摂氏温度で温度を保存することができるクラスを作ることにあなたが決めると仮定しよう。
class Celsius:
def __init__(self, temperature = 0):
self.set_temperature(temperature)
def to_Fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
def get_temperature(self):
return self._temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value
リファクタリングされたコード、これがプロパティでそれを達成することができた方法です。
Pythonでは、property()はプロパティオブジェクトを作成して返す組み込み関数です。
プロパティオブジェクトには、getter()、setter()、およびdelete()の3つのメソッドがあります。
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_Fahrenheit(self):
return (self.temperature * 1.8) + 32
def get_temperature(self):
print("Getting value")
return self.temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self.temperature = value
temperature = property(get_temperature,set_temperature)
ここに、
temperature = property(get_temperature,set_temperature)
に分解された可能性があります、
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
注意すべき点:
今、あなたは書くことによって温度の値にアクセスすることができます。
C = Celsius()
C.temperature
# instead of writing C.get_temperature()
先に進むと、get_temperatureとset_temperatureという名前は不要であり、クラスの名前空間を汚染するので定義しないでください。
上記の問題に対処するための Pythonicの方法 は、 @property を使用することです。
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_Fahrenheit(self):
return (self.temperature * 1.8) + 32
@property
def temperature(self):
print("Getting value")
return self.temperature
@temperature.setter
def temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self.temperature = value
注意点 -
お分かりのように、コードは間違いなくエレガントさを増しています。
それでは、実際の現実的な場面について話しましょう。
次のようにクラスを設計したとしましょう。
class OurClass:
def __init__(self, a):
self.x = a
y = OurClass(10)
print(y.x)
さて、私たちのクラスがクライアントの間で人気が出て、彼らが彼らのプログラムでそれを使い始めたとさらに仮定しよう。彼らはオブジェクトにあらゆる種類の割り当てをした。
そしてある運命の日、信頼できるクライアントが私たちのところにやってきて、 "x"は0から1000の間の値でなければならないと示唆しました、これは本当に恐ろしいシナリオです!
プロパティのせいで簡単です。プロパティバージョンの "x"を作成します。
class OurClass:
def __init__(self,x):
self.x = x
@property
def x(self):
return self.__x
@x.setter
def x(self, x):
if x < 0:
self.__x = 0
Elif x > 1000:
self.__x = 1000
else:
self.__x = x
これは素晴らしいことです、そうではありません:あなたが想像できる最も簡単な実装から始めることができます、そしてあなたは後でインターフェースを変更する必要なしにプロパティバージョンに自由に移行することができます!だからプロパティは単にゲッターやセッターの代わりになるものではありません!
あなたはこの実装をチェックすることができます ここ
この点は多くの人々によって明らかにされていますが、ここで私が探していた直接的な点があります。
class UtilityMixin():
@property
def get_config(self):
return "This is property"
関数 "get_config()"の呼び出しはこのように動作します。
util = UtilityMixin()
print(util.get_config)
あなたが気付くならば、私は関数を呼び出すために "()"括弧を使っていません。これは私が@propertyデコレータを探していた基本的なものです。関数を変数のように使うことができます。
property
は@property
デコレータの背後にあるクラスです。
あなたはいつでもこれをチェックすることができます:
print(property) #<class 'property'>
例をhelp(property)
から書き直して、@property
構文が正しいことを示します。
class C:
def __init__(self):
self._x=None
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
c = C()
c.x="a"
print(c.x)
機能的にはproperty()
の構文と同じです。
class C:
def __init__(self):
self._x=None
def g(self):
return self._x
def s(self, v):
self._x = v
def d(self):
del self._x
prop = property(g,s,d)
c = C()
c.x="a"
print(c.x)
ご覧のとおり、このプロパティの使用方法に違いはありません。
@property
デコレータはproperty
クラスを介して実装されています。だから、問題はproperty
クラスを少し説明することです。この行
prop = property(g,s,d)
初期化でした。このように書き直すことができます。
prop = property(fget=g,fset=s,fdel=d)
fget
、fset
およびfdel
の意味
| fget
| function to be used for getting an attribute value
| fset
| function to be used for setting an attribute value
| fdel
| function to be used for del'ing an attribute
| doc
| docstring
次の図は、クラスproperty
から取得したトリプレットを示しています。
__get__
、__set__
、および__delete__
は、 オーバーライドされる です。これはPythonの記述子パターンの実装です。
一般に、記述子は「バインディング動作」を持つオブジェクト属性であり、その属性アクセスは記述子プロトコルのメソッドによって上書きされています。
プロパティのsetter
、getter
、deleter
メソッドを使って関数をpropertyにバインドすることもできます。次の例を確認してください。クラスC
のメソッドs2
は、プロパティ doubled を設定します。
class C:
def __init__(self):
self._x=None
def g(self):
return self._x
def s(self, x):
self._x = x
def d(self):
del self._x
def s2(self,x):
self._x=x+x
x=property(g)
x=x.setter(s)
x=x.deleter(d)
c = C()
c.x="a"
print(c.x) # outputs "a"
C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"
プロパティは2つの方法で宣言できます。
pythonのプロパティ について書いたいくつかの例を見ることができます。
これは別の例です。
##
## Python Properties Example
##
class GetterSetterExample( object ):
## Set the default value for x ( we reference it using self.x, set a value using self.x = value )
__x = None
##
## On Class Initialization - do something... if we want..
##
def __init__( self ):
## Set a value to __x through the getter / setter... Since __x is defined above, this doesn't need to be set...
self.x = 1234
return None
##
## Define x as a property, ie a getter - All getters should have a default value arg, so I added it - it will not be passed in when setting a value, so you need to set the default here so it will be used..
##
@property
def x( self, _default = None ):
## I added an optional default value argument as all getters should have this - set it to the default value you want to return...
_value = ( self.__x, _default )[ self.__x == None ]
## Debugging - so you can see the order the calls are made...
print( '[ Test Class ] Get x = ' + str( _value ) )
## Return the value - we are a getter afterall...
return _value
##
## Define the setter function for x...
##
@x.setter
def x( self, _value = None ):
## Debugging - so you can see the order the calls are made...
print( '[ Test Class ] Set x = ' + str( _value ) )
## This is to show the setter function works.... If the value is above 0, set it to a negative value... otherwise keep it as is ( 0 is the only non-negative number, it can't be negative or positive anyway )
if ( _value > 0 ):
self.__x = -_value
else:
self.__x = _value
##
## Define the deleter function for x...
##
@x.deleter
def x( self ):
## Unload the assignment / data for x
if ( self.__x != None ):
del self.__x
##
## To String / Output Function for the class - this will show the property value for each property we add...
##
def __str__( self ):
## Output the x property data...
print( '[ x ] ' + str( self.x ) )
## Return a new line - technically we should return a string so it can be printed where we want it, instead of printed early if _data = str( C( ) ) is used....
return '\n'
##
##
##
_test = GetterSetterExample( )
print( _test )
## For some reason the deleter isn't being called...
del _test.x
基本的に、C(オブジェクト)の例と同じですが、代わりに xを使用しています。__init - で初期化していません。はクラスの一部として定義されています....
出力は以下のとおりです。
[ Test Class ] Set x = 1234
[ Test Class ] Get x = -1234
[ x ] -1234
init でself.x = 1234をコメントアウトすると、出力は次のようになります。
[ Test Class ] Get x = None
[ x ] None
また、getter関数で_default = Noneを_default = 0に設定した場合(すべてのgetterはデフォルト値を持つはずですが、ここで定義したプロパティ値では渡されないため、ここで定義できます)。デフォルトを一度定義してどこでも使うことができるので、実際にはそれほど悪くありません。例:def x(self、_default = 0):
[ Test Class ] Get x = 0
[ x ] 0
注:ゲッターロジックは、値が操作されることを保証するために値によって操作されるようにするためのものです - printステートメントでも同じです。
注:私はLuaに慣れていて、1つの関数を呼び出すときに動的に10+ヘルパーを作成することができ、プロパティを使用せずにPython用に似たようなものを作成しました。それがそのようにコード化されていないので変わっている...それらが作成される前に呼び出されることに関して時々問題がまだ時々あります。基本的に直接変数にアクセスするのではなく... Pythonを使っていくつかのことをどれだけ早く構築できるかが好きです - 例えばguiプログラム。私が設計しているものは、追加のライブラリがたくさんなければ実現できないかもしれません - それをAutoHotkeyでコーディングすれば、必要なdll呼び出しに直接アクセスでき、Java、C#、C++などでも同じことができます。正しいことがまだ見つかっていませんが、そのプロジェクトではPythonから変更することができます。
注意:このフォーラムのコード出力は壊れています - それを機能させるためにコードの最初の部分にスペースを追加しなければなりませんでした - コピー/ペーストで確実にすべてのスペースをタブに変換してください。ファイルサイズが10,000行のファイルは、スペースで512KBから1MB、タブで100から200KBであり、ファイルサイズの大幅な違いと処理時間の短縮に相当します。
タブはユーザーごとに調整することもできます - したがって、2スペース、4、8など、できることなら何でも好きな人には、視力障害のある開発者にとっては思慮深いことです。
注:フォーラムソフトウェアのバグのため、クラスで定義されているすべての関数が正しくインデントされていません。コピーして貼り付ける場合は必ずインデントしてください。
私にとっては、Python 2.xでは@propertyがフォームオブジェクトを継承していないときの宣伝どおりに機能しませんでした。
class A():
pass
しかし、うまくいったとき:
class A(object):
pass
pyhton 3のために、いつも働いて