574

Python で C のような構造を便利に定義する方法はありますか? 私は次のようなものを書くのにうんざりしています:

class MyStruct():
    def __init__(self, field1, field2, field3):
        self.field1 = field1
        self.field2 = field2
        self.field3 = field3
4

25 に答える 25

380

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")
于 2008-08-30T15:18:59.613 に答える
102

タプルは、C で構造体を使用する多くの場合に使用できます (たとえば、x、y 座標や RGB カラーなど)。

それ以外の場合は、辞書または次のようなユーティリティ クラスを使用できます。

>>> class Bunch:
...     def __init__(self, **kwds):
...         self.__dict__.update(kwds)
...
>>> mystruct = Bunch(field1=value1, field2=value2)

「決定的な」議論は、公開されたバージョンの Python Cookbook にあると思います。

于 2008-08-30T14:38:38.447 に答える
86

おそらく、コンストラクターのない構造体を探しているでしょう:

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
于 2010-09-21T15:15:33.527 に答える
71

辞書はどうですか?

このようなもの:

myStruct = {'field1': 'some val', 'field2': 'some val'}

次に、これを使用して値を操作できます。

print myStruct['field1']
myStruct['field2'] = 'some other values'

また、値は文字列である必要はありません。それらは、他のほとんどすべてのオブジェクトである可能性があります。

于 2008-08-30T14:35:29.423 に答える
28

dF:それはかなりクールです...dictを使用してクラスのフィールドにアクセスできることを知りませんでした。

マーク:私がこれを望んでいた状況は、まさにタプルが必要なときですが、辞書ほど「重い」ものはありません。

クラスのフィールド、そのメソッド、およびそのすべてのプロパティはdictを使用して内部的に格納されるため(少なくともCPythonでは)、ディクショナリを使用してクラスのフィールドにアクセスできます。

...これが2番目のコメントにつながります。Pythonの辞書が「重い」と信じることは、非常に非Python的な概念です。そして、そのようなコメントを読むと、私のPythonZenが殺されます。それは良いことではありません。

ご覧のとおり、クラスを宣言すると、実際には辞書の周りにかなり複雑なラッパーが作成されます。つまり、どちらかといえば、単純な辞書を使用するよりもオーバーヘッドが増えます。ちなみに、どのような場合でも意味のないオーバーヘッド。パフォーマンスが重要なアプリケーションで作業している場合は、Cなどを使用してください。

于 2008-08-30T15:20:15.150 に答える
21

標準ライブラリで利用可能な 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
>>>
于 2015-06-25T23:50:17.610 に答える
19

位置によってインスタンス変数に初期化パラメータを渡すこともできます

# 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
于 2008-08-30T15:53:10.437 に答える
13

「辞書のように動作するインスタント データ オブジェクト」が必要なときはいつでも ( 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'])

「クラスではないデータバッグ」が必要な場合や、名前付きタプルが理解できない場合に非常に便利です...

于 2013-09-13T17:40:45.253 に答える
10

ここでのいくつかの答えは非常に精巧です。私が見つけた最も簡単なオプションは次のとおりです(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

編集:申し訳ありませんが、この例はさらに下に表示されていません。

于 2017-08-05T00:39:50.573 に答える
5

ここにはこの答えが表示されないので、現在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ですが、成功します。それでも、それは機能します。

于 2016-11-08T14:06:05.183 に答える
5

これは少し遅いかもしれませんが、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
>>> 
于 2015-03-23T14:32:31.187 に答える
2

構造体に対する次のソリューションは、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'
于 2018-03-29T16:25:15.180 に答える