目標は、db結果セットのように動作する模擬クラスを作成することです。
したがって、たとえば、{'ab':100, 'cd':200}
dict式を使用してデータベースクエリが返された場合、次のようになります。
>>> dummy.ab
100
最初は、この方法でできると思っていました。
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
def __init__(self, ks, vs):
for i, k in enumerate(ks):
self[k] = vs[i]
setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))
def fn_readonly(self, v)
raise "It is ready only"
if __== "__main__":
c = C(ks, vs)
print c.ab
しかし、c.ab
は代わりにプロパティオブジェクトを返します。
setattr
行をk = property(lambda x: vs[i])
に置き換えることはまったく役に立ちません。
では、実行時にインスタンスプロパティを作成する正しい方法は何ですか?
私は年長で賢く、何が起こっているかを知っているので、この答えを広げるべきだと思います。絶対に遅れることはありません。
canプロパティをクラスに動的に追加します。しかし、それはキャッチです:classに追加する必要があります。
>>> class Foo(object):
... pass
...
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4
property
は、実際には descriptor と呼ばれるものの単純な実装です。これは、特定の属性特定のクラスのカスタム処理を提供するオブジェクトです。 __getattribute__
から巨大なif
ツリーを分解する方法が好きです。
上記の例でfoo.b
を要求すると、Pythonはクラスで定義されたb
がdescriptor protocolを実装していることを確認します。つまり、__get__
、__set__
、 __delete__
メソッド。記述子はその属性を処理する責任を主張するため、PythonはFoo.b.__get__(foo, Foo)
を呼び出し、戻り値は属性の値として返されます。 property
の場合、これらの各メソッドはfget
コンストラクターに渡したfset
、fdel
、またはproperty
を呼び出すだけです。
記述子は、実際にOO実装全体の配管を公開するPythonの方法です。実際、property
よりもさらに一般的なタイプの記述子があります。
>>> class Foo(object):
... def bar(self):
... pass
...
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>
謙虚な方法は単なる別の種類の記述子です。その__get__
は、最初の引数として呼び出し元インスタンスにタックします。実際には、次のことを行います。
def __get__(self, instance, owner):
return functools.partial(self.function, instance)
とにかく、これが記述子がクラスでのみ機能する理由だと思います:そもそもクラスを動かすものの形式化です。それらはルールの例外でもあります。明らかに記述子をクラスに割り当てることができ、クラス自体はtype
のインスタンスです!実際、Foo.b
を読み取ろうとすると、property.__get__
が呼び出されます。記述子がクラス属性としてアクセスされたときに自分自身を返すことは単なる慣用です。
PythonのOOシステムのほぼすべてをPythonで表現できるのはかなりクールだと思います。 :)
ああ、そして、私は 記述子に関する言葉の多いブログ投稿 興味があるならしばらく前に書いた。
目標は、db結果セットのように動作する模擬クラスを作成することです。
それで、a ['b']をa.bとして綴ることができる辞書が必要ですか?
簡単だ:
class atdict(dict):
__getattr__= dict.__getitem__
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
namedtuple
を使用すると、この問題をはるかに簡単に解決できるようです。これは、フィールドのリスト全体が事前にわかっているためです。
from collections import namedtuple
Foo = namedtuple('Foo', ['bar', 'quux'])
foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux
foo2 = Foo() # error
独自のセッターを作成する必要がある場合は、クラスレベルでメタプログラミングを行う必要があります。 property()
はインスタンスでは機能しません。
そのためにプロパティを使用する必要はありません。 __setattr__
をオーバーライドして、読み取り専用にします。
class C(object):
def __init__(self, keys, values):
for (key, value) in Zip(keys, values):
self.__dict__[key] = value
def __setattr__(self, name, value):
raise Exception("It is read only!")
多田。
>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __setattr__
Exception: It is read only!
プロパティをpythonクラスに動的に追加する方法は?
プロパティを追加するオブジェクトがあるとします。通常、一貫性のあるAPIを維持できるように、ダウンストリームで使用されるコード内の属性へのアクセスの管理を開始する必要があるときにプロパティを使用します。通常、オブジェクトが定義されているソースコードにそれらを追加しますが、そのアクセス権がないか、プログラムで関数を本当に動的に選択する必要があると仮定しましょう。
property
のドキュメント に基づいた例を使用して、「hidden」属性を持つオブジェクトのクラスを作成し、そのインスタンスを作成しましょう。
class C(object):
'''basic class'''
_x = None
o = C()
Pythonでは、物事を行うための1つの明らかな方法があると期待しています。ただし、この場合、デコレータ表記法を使用する場合と使用しない場合の2つの方法を示します。まず、デコレータ表記なし。これは、getter、setter、またはdeleterの動的割り当てに役立つ場合があります。
クラス用にいくつか作成しましょう:
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
そして、これらをプロパティに割り当てます。ここでは、動的な質問に答えて、プログラムで関数を選択できることに注意してください。
C.x = property(getx, setx, delx, "I'm the 'x' property.")
そして使用法:
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:
I'm the 'x' property.
上記のデコレータ表記法と同じことができますが、この場合は、mustメソッドにすべて同じ名前を付けます(そして、属性と同じにすることをお勧めします)。そのため、プログラムによる割り当ては上記の方法を使用するほど簡単ではありません。
@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
そして、プロビジョンされたセッターと削除機能を持つプロパティオブジェクトをクラスに割り当てます。
C.x = x
そして使用法:
>>> help(C.x)
Help on property:
I'm the 'x' property.
>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
同様の質問をしました このStack Overflowの投稿 単純型を作成するクラスファクトリを作成します。結果は この回答 であり、クラスファクトリの作業バージョンがありました。これが答えの断片です。
def Struct(*args, **kwargs):
def init(self, *iargs, **ikwargs):
for k,v in kwargs.items():
setattr(self, k, v)
for i in range(len(iargs)):
setattr(self, args[i], iargs[i])
for k,v in ikwargs.items():
setattr(self, k, v)
name = kwargs.pop("name", "MyStruct")
kwargs.update(dict((k, None) for k in args))
return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})
>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>
これのいくつかのバリエーションを使用して、目標であるデフォルト値を作成できます(この質問には、これに対処する答えもあります)。
プロパティはデータ記述子であるため、実行時に新しいproperty()
をインスタンスに追加することはできません。代わりに、新しいクラスを動的に作成するか、インスタンスのデータ記述子を処理するために__getattribute__
をオーバーロードする必要があります。
検索エンジンから来た人のために、dynamicプロパティについて話すときに私が探していた2つのものがあります:
class Foo:
def __init__(self):
# we can dynamically have access to the properties dict using __dict__
self.__dict__['foo'] = 'bar'
assert Foo().foo == 'bar'
# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
def __init__(self):
self._data = {}
def __getattr__(self, key):
return self._data[key]
def __setattr__(self, key, value):
self._data[key] = value
bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'
__dict__
は、動的に作成されたプロパティを配置する場合に適しています。 __getattr__
は、データベースへのクエリなど、値が必要な場合にのみ何かをするのに適しています。 set/getコンボは、クラスに格納されたデータへのアクセスを簡単にするのに適しています(上記の例のように)。
動的プロパティが1つだけ必要な場合は、 property() 組み込み関数をご覧ください。
私が質問を完全に理解しているかどうかはわかりませんが、クラスの組み込み__dict__
を使用して実行時にインスタンスプロパティを変更できます。
class C(object):
def __init__(self, ks, vs):
self.__dict__ = dict(Zip(ks, vs))
if __== "__main__":
ks = ['ab', 'cd']
vs = [12, 34]
c = C(ks, vs)
print(c.ab) # 12
達成する最良の方法は、__slots__
を定義することです。そのようにして、インスタンスに新しい属性を持たせることはできません。
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
__slots__ = []
def __init__(self, ks, vs): self.update(Zip(ks, vs))
def __getattr__(self, key): return self[key]
if __== "__main__":
c = C(ks, vs)
print c.ab
それは12
を出力します
c.ab = 33
それは与える:AttributeError: 'C' object has no attribute 'ab'
望ましい効果を達成する方法の別の例
class Foo(object):
_bar = None
@property
def bar(self):
return self._bar
@bar.setter
def bar(self, value):
self._bar = value
def __init__(self, dyn_property_name):
setattr(Foo, dyn_property_name, Foo.bar)
そのため、次のようなことができます。
>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5
次のコードを使用して、辞書オブジェクトを使用してクラス属性を更新できます。
class ExampleClass():
def __init__(self, argv):
for key, val in argv.items():
self.__dict__[key] = val
if __== '__main__':
argv = {'intro': 'Hello World!'}
instance = ExampleClass(argv)
print instance.intro
これはOPが望んだものとは少し異なりますが、実用的な解決策を得るまで脳をガタガタ鳴らしたので、次の人/ギャルのためにここに置いています
動的なセッターとゲッターを指定する方法が必要でした。
class X:
def __init__(self, a=0, b=0, c=0):
self.a = a
self.b = b
self.c = c
@classmethod
def _make_properties(cls, field_name, inc):
_inc = inc
def _get_properties(self):
if not hasattr(self, '_%s_inc' % field_name):
setattr(self, '_%s_inc' % field_name, _inc)
inc = _inc
else:
inc = getattr(self, '_%s_inc' % field_name)
return getattr(self, field_name) + inc
def _set_properties(self, value):
setattr(self, '_%s_inc' % field_name, value)
return property(_get_properties, _set_properties)
事前に自分のフィールドを知っているので、自分のプロパティを作成します。注:このPERインスタンスを実行することはできません。これらのプロパティはクラスに存在します!!!
for inc, field in enumerate(['a', 'b', 'c']):
setattr(X, '%s_summed' % field, X._make_properties(field, inc))
すべてテストしましょう。
x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0
assert x.a_summed == 0 # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1 # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2 # enumerate() set inc to 2 + 0 = 2
# we set the variables to something
x.a = 1
x.b = 2
x.c = 3
assert x.a_summed == 1 # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3 # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5 # enumerate() set inc to 2 + 3 = 5
# we're changing the inc now
x.a_summed = 1
x.b_summed = 3
x.c_summed = 5
assert x.a_summed == 2 # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5 # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8 # we set inc to 5 + the property was 3 = 8
紛らわしいですか?はい、申し訳ありませんが、意味のある実世界の例が思いつきませんでした。また、これは心の明るい人のためではありません。
これは動作するようです(ただし、以下を参照):
class data(dict,object):
def __init__(self,*args,**argd):
dict.__init__(self,*args,**argd)
self.__dict__.update(self)
def __setattr__(self,name,value):
raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
def __delattr__(self,name):
raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
より複雑な動作が必要な場合は、回答を自由に編集してください。
大規模なデータセットの場合、おそらく次の方がメモリ効率が高くなります。
class data(dict,object):
def __init__(self,*args,**argd):
dict.__init__(self,*args,**argd)
def __getattr__(self,name):
return self[name]
def __setattr__(self,name,value):
raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
def __delattr__(self,name):
raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
あなたの質問の主な目的に答えるために、不変のデータソースとして辞書からの読み取り専用属性が必要です:
目標は、db結果セットのように動作する模擬クラスを作成することです。
たとえば、データベースクエリが返された場合、dict式
{'ab':100, 'cd':200}
を使用すると、>>> dummy.ab 100
namedtuple
モジュールのcollections
を使用して、これを実現する方法を示します。
import collections
data = {'ab':100, 'cd':200}
def maketuple(d):
'''given a dict, return a namedtuple'''
Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
return Tup(**d)
dummy = maketuple(data)
dummy.ab
100
を返します
多くの回答がありますが、満足できるものは見つかりませんでした。 property
を動的なケースで機能させる独自のソリューションを見つけました。元の質問に答えるソース:
#!/usr/local/bin/python3
INITS = { 'ab': 100, 'cd': 200 }
class DP(dict):
def __init__(self):
super().__init__()
for k,v in INITS.items():
self[k] = v
def _dict_set(dp, key, value):
dp[key] = value
for item in INITS.keys():
setattr(
DP,
item,
lambda key: property(
lambda self: self[key], lambda self, value: _dict_set(self, key, value)
)(item)
)
a = DP()
print(a) # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False
私のために働くものはこれです:
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,s,d)
c = C()
c.x="a"
print(c.x)
C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)
出力
a
aa
kjfletch からアイデアを拡張する
# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.
def Struct(*args, **kwargs):
FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
types.FunctionType, types.MethodType)
def init(self, *iargs, **ikwargs):
"""Asume that unamed args are placed in the same order than
astuple() yields (currently alphabetic order)
"""
kw = list(self.__slots__)
# set the unnamed args
for i in range(len(iargs)):
k = kw.pop(0)
setattr(self, k, iargs[i])
# set the named args
for k, v in ikwargs.items():
setattr(self, k, v)
kw.remove(k)
# set default values
for k in kw:
v = kwargs[k]
if isinstance(v, FUNCTIONS):
v = v()
setattr(self, k, v)
def astuple(self):
return Tuple([getattr(self, k) for k in self.__slots__])
def __str__(self):
data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))
def __repr__(self):
return str(self)
def __eq__(self, other):
return self.astuple() == other.astuple()
name = kwargs.pop("__name__", "MyStruct")
slots = list(args)
slots.extend(kwargs.keys())
# set non-specific default values to None
kwargs.update(dict((k, None) for k in args))
return type(name, (object,), {
'__init__': init,
'__slots__': Tuple(slots),
'astuple': astuple,
'__str__': __str__,
'__repr__': __repr__,
'__eq__': __eq__,
})
Event = Struct('user', 'cmd', \
'arg1', 'arg2', \
date=time.time, \
__name__='Event')
aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()
bb = Event(*raw)
print(bb)
if aa == bb:
print('Are equals')
cc = Event(cmd='foo')
print(cc)
出力:
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>
class atdict(dict):
def __init__(self, value, **kwargs):
super().__init__(**kwargs)
self.__dict = value
def __getattr__(self, name):
for key in self.__dict:
if type(self.__dict[key]) is list:
for idx, item in enumerate(self.__dict[key]):
if type(item) is dict:
self.__dict[key][idx] = atdict(item)
if type(self.__dict[key]) is dict:
self.__dict[key] = atdict(self.__dict[key])
return self.__dict[name]
d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})
print(d1.a.b[0].c)
出力は次のとおりです。
>> 1