Python で C のような構造を便利に定義する方法はありますか? 私は次のようなものを書くのにうんざりしています:
class MyStruct():
def __init__(self, field1, field2, field3):
self.field1 = field1
self.field2 = field2
self.field3 = field3
Python2.6の標準ライブラリのコレクションモジュールに追加された名前付きタプルを使用します。Python 2.4をサポートする必要がある場合は、RaymondHettingerの名前付きタプルレシピを使用することもできます。
これは基本的な例としては便利ですが、後で遭遇する可能性のある一連のエッジケースについても説明します。上記のフラグメントは次のように記述されます。
from collections import namedtuple
MyStruct = namedtuple("MyStruct", "field1 field2 field3")
新しく作成されたタイプは、次のように使用できます。
m = MyStruct("foo", "bar", "baz")
名前付き引数を使用することもできます。
m = MyStruct(field1="foo", field2="bar", field3="baz")
タプルは、C で構造体を使用する多くの場合に使用できます (たとえば、x、y 座標や RGB カラーなど)。
それ以外の場合は、辞書または次のようなユーティリティ クラスを使用できます。
>>> class Bunch:
... def __init__(self, **kwds):
... self.__dict__.update(kwds)
...
>>> mystruct = Bunch(field1=value1, field2=value2)
「決定的な」議論は、公開されたバージョンの Python Cookbook にあると思います。
おそらく、コンストラクターのない構造体を探しているでしょう:
class Sample:
name = ''
average = 0.0
values = None # list cannot be initialized here!
s1 = Sample()
s1.name = "sample 1"
s1.values = []
s1.values.append(1)
s1.values.append(2)
s1.values.append(3)
s2 = Sample()
s2.name = "sample 2"
s2.values = []
s2.values.append(4)
for v in s1.values: # prints 1,2,3 --> OK.
print v
print "***"
for v in s2.values: # prints 4 --> OK.
print v
辞書はどうですか?
このようなもの:
myStruct = {'field1': 'some val', 'field2': 'some val'}
次に、これを使用して値を操作できます。
print myStruct['field1']
myStruct['field2'] = 'some other values'
また、値は文字列である必要はありません。それらは、他のほとんどすべてのオブジェクトである可能性があります。
dF:それはかなりクールです...dictを使用してクラスのフィールドにアクセスできることを知りませんでした。
マーク:私がこれを望んでいた状況は、まさにタプルが必要なときですが、辞書ほど「重い」ものはありません。
クラスのフィールド、そのメソッド、およびそのすべてのプロパティはdictを使用して内部的に格納されるため(少なくともCPythonでは)、ディクショナリを使用してクラスのフィールドにアクセスできます。
...これが2番目のコメントにつながります。Pythonの辞書が「重い」と信じることは、非常に非Python的な概念です。そして、そのようなコメントを読むと、私のPythonZenが殺されます。それは良いことではありません。
ご覧のとおり、クラスを宣言すると、実際には辞書の周りにかなり複雑なラッパーが作成されます。つまり、どちらかといえば、単純な辞書を使用するよりもオーバーヘッドが増えます。ちなみに、どのような場合でも意味のないオーバーヘッド。パフォーマンスが重要なアプリケーションで作業している場合は、Cなどを使用してください。
標準ライブラリで利用可能な C 構造体をサブクラス化できます。ctypesモジュールはStructure クラスを提供します。ドキュメントの例:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = [("x", c_int),
... ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print point.x, point.y
10 20
>>> point = POINT(y=5)
>>> print point.x, point.y
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: too many initializers
>>>
>>> class RECT(Structure):
... _fields_ = [("upperleft", POINT),
... ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print rc.upperleft.x, rc.upperleft.y
0 5
>>> print rc.lowerright.x, rc.lowerright.y
0 0
>>>
位置によってインスタンス変数に初期化パラメータを渡すこともできます
# Abstract struct class
class Struct:
def __init__ (self, *argv, **argd):
if len(argd):
# Update by dictionary
self.__dict__.update (argd)
else:
# Update by position
attrs = filter (lambda x: x[0:2] != "__", dir(self))
for n in range(len(argv)):
setattr(self, attrs[n], argv[n])
# Specific class
class Point3dStruct (Struct):
x = 0
y = 0
z = 0
pt1 = Point3dStruct()
pt1.x = 10
print pt1.x
print "-"*10
pt2 = Point3dStruct(5, 6)
print pt2.x, pt2.y
print "-"*10
pt3 = Point3dStruct (x=1, y=2, z=3)
print pt3.x, pt3.y, pt3.z
print "-"*10
「辞書のように動作するインスタント データ オブジェクト」が必要なときはいつでも ( C 構造体については考えていません!)、次のかわいいハックを思い浮かべます。
class Map(dict):
def __init__(self, **kwargs):
super(Map, self).__init__(**kwargs)
self.__dict__ = self
今、あなたはただ言うことができます:
struct = Map(field1='foo', field2='bar', field3=42)
self.assertEquals('bar', struct.field2)
self.assertEquals(42, struct['field3'])
「クラスではないデータバッグ」が必要な場合や、名前付きタプルが理解できない場合に非常に便利です...
ここでのいくつかの答えは非常に精巧です。私が見つけた最も簡単なオプションは次のとおりです(http://norvig.com/python-iaq.htmlから):
class Struct:
"A structure that can have any fields defined."
def __init__(self, **entries): self.__dict__.update(entries)
初期化中:
>>> options = Struct(answer=42, linelen=80, font='courier')
>>> options.answer
42
さらに追加:
>>> options.cat = "dog"
>>> options.cat
dog
編集:申し訳ありませんが、この例はさらに下に表示されていません。
ここにはこの答えが表示されないので、現在Pythonを学習していて発見したばかりなので、追加すると思います。Python チュートリアル(この場合は Python 2) は、次の単純で効果的な例を示しています。
class Employee:
pass
john = Employee() # Create an empty employee record
# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
つまり、空のクラス オブジェクトが作成されてからインスタンス化され、フィールドが動的に追加されます。
これの利点は、その非常に単純なことです。欠点は、それが特に自己文書化されていないことです (意図したメンバーはクラスの "定義" のどこにもリストされていません)、未設定のフィールドはアクセス時に問題を引き起こす可能性があります。これら 2 つの問題は、次の方法で解決できます。
class Employee:
def __init__ (self):
self.name = None # or whatever
self.dept = None
self.salary = None
これで、少なくともプログラムが期待するフィールドを一目で確認できます。
どちらもタイプミスしやすいjohn.slarly = 1000
ですが、成功します。それでも、それは機能します。
これは少し遅いかもしれませんが、Python メタクラス (以下のデコレータ バージョンも) を使用して解決策を作成しました。
が実行時__init__
に呼び出されると、各引数とその値が取得され、インスタンス変数としてクラスに割り当てられます。このようにして、すべての値を手動で割り当てることなく、構造体のようなクラスを作成できます。
私の例にはエラーチェックがないので、従うのが簡単です。
class MyStruct(type):
def __call__(cls, *args, **kwargs):
names = cls.__init__.func_code.co_varnames[1:]
self = type.__call__(cls, *args, **kwargs)
for name, value in zip(names, args):
setattr(self , name, value)
for name, value in kwargs.iteritems():
setattr(self , name, value)
return self
ここでそれが実行されます。
>>> class MyClass(object):
__metaclass__ = MyStruct
def __init__(self, a, b, c):
pass
>>> my_instance = MyClass(1, 2, 3)
>>> my_instance.a
1
>>>
私はそれを reddit に投稿し、/u/matchuはよりクリーンなデコレータ バージョンを投稿しました。メタクラスのバージョンを拡張したい場合を除き、これを使用することをお勧めします。
>>> def init_all_args(fn):
@wraps(fn)
def wrapped_init(self, *args, **kwargs):
names = fn.func_code.co_varnames[1:]
for name, value in zip(names, args):
setattr(self, name, value)
for name, value in kwargs.iteritems():
setattr(self, name, value)
return wrapped_init
>>> class Test(object):
@init_all_args
def __init__(self, a, b):
pass
>>> a = Test(1, 2)
>>> a.a
1
>>>
構造体に対する次のソリューションは、namedtuple 実装と以前の回答のいくつかに触発されています。ただし、namedtuple とは異なり、その値は変更可能ですが、通常のクラスまたは dict では変更できない名前/属性で変更できない C スタイルの構造体と同様です。
_class_template = """\
class {typename}:
def __init__(self, *args, **kwargs):
fields = {field_names!r}
for x in fields:
setattr(self, x, None)
for name, value in zip(fields, args):
setattr(self, name, value)
for name, value in kwargs.items():
setattr(self, name, value)
def __repr__(self):
return str(vars(self))
def __setattr__(self, name, value):
if name not in {field_names!r}:
raise KeyError("invalid name: %s" % name)
object.__setattr__(self, name, value)
"""
def struct(typename, field_names):
class_definition = _class_template.format(
typename = typename,
field_names = field_names)
namespace = dict(__name__='struct_%s' % typename)
exec(class_definition, namespace)
result = namespace[typename]
result._source = class_definition
return result
使用法:
Person = struct('Person', ['firstname','lastname'])
generic = Person()
michael = Person('Michael')
jones = Person(lastname = 'Jones')
In [168]: michael.middlename = 'ben'
Traceback (most recent call last):
File "<ipython-input-168-b31c393c0d67>", line 1, in <module>
michael.middlename = 'ben'
File "<string>", line 19, in __setattr__
KeyError: 'invalid name: middlename'