PythonのCのような構造


446

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

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

5
半関連して、代数的データ型は絶対に素晴らしいですが、それらをうまく使用するには、通常パターンマッチングが必要です。
エドワードZ.ヤン

50
このメソッドには、書くのが面倒なこと以外に何か問題がありますか?
レベスク

2
あなたはdstructが役に立つと思うかもしれません: github.com/dorkitude/dstruct
カイルワイルド

10
@levesqueは、タイプミスなしでリファクタリングするのが難しく、コードをスキミングするときに一目で読むのが難しいMyStruct = namedtuple("MyStruct", "field1 field2 field3")
sam boosalis 14

1
pandas.Series(a=42).aデータサイエンティストならこれを行うべきです...
Mark Horvath

回答:


341

Python 2.6の標準ライブラリのコレクションモジュールに追加された名前付きタプルを使用します。Python 2.4をサポートする必要がある場合は、Raymond Hettingerの名前付きタプルレシピを使用することもできます。

基本的な例としてはすばらしいだけでなく、後で遭遇する可能性がある一連のエッジケースもカバーしています。上記のフラグメントは次のように記述されます。

from collections import namedtuple
MyStruct = namedtuple("MyStruct", "field1 field2 field3")

新しく作成されたタイプは次のように使用できます:

m = MyStruct("foo", "bar", "baz")

名前付き引数を使用することもできます。

m = MyStruct(field1="foo", field2="bar", field3="baz")

164
...しかし、namedtupleは不変です。OPの例は変更可能です。
mhowison 2013年

28
@mhowison-私の場合、それはプラスです。
ArtOfWarfare 14

3
素晴らしい解決策。これらのタプルの配列をどのようにループしますか?フィールド1〜3は、タプルオブジェクト全体で同じ名前である必要があると思います。
マイケル・スミス

2
namedtupleは最大4つの引数を持つことができるので、対応するnamedtupleでより多くのデータメンバーを持つ構造をマップする方法
Kapil

3
@Kapil-namedtupleの2番目の引数は、メンバーの名前のリストでなければなりません。そのリストは任意の長さにすることができます。
ArtOfWarfare 2017年

224

更新:データクラス

導入によりデータクラスのPython 3.7我々は非常に近い取得します。

次の例は、以下のNamedTupleの例に似ていますが、結果のオブジェクトは可変であり、デフォルト値を許可しています。

from dataclasses import dataclass


@dataclass
class Point:
    x: float
    y: float
    z: float = 0.0


p = Point(1.5, 2.5)

print(p)  # Point(x=1.5, y=2.5, z=0.0)

これは、より具体的な型注釈を使用したい場合に備えて、新しい型付けモジュールでうまく機能します。

私はこれを必死に待っていました!私に尋ねると、データクラスと新しいNamedTuple宣言を、型付けモジュールと組み合わせると、すばらしい結果が得られます

NamedTuple宣言の改善

以来のPython 3.6それは限り、あなたが一緒に暮らすことができるよう、(私見)非常にシンプルで美しいなった不変

NamedTuplesを宣言するための新しい方法をを可能にする、導入された型の注釈にも:

from typing import NamedTuple


class User(NamedTuple):
    name: str


class MyStruct(NamedTuple):
    foo: str
    bar: int
    baz: list
    qux: User


my_item = MyStruct('foo', 0, ['baz'], User('peter'))

print(my_item) # MyStruct(foo='foo', bar=0, baz=['baz'], qux=User(name='peter'))

6
メイト、あなたは私の日を作りました-不変の
dicts-

10
このdataclassモジュールはPython 3.7の新機能ですが、できpip install dataclassesます。Python 3.6のバックポートです。pypi.org/project/dataclasses/#description
Lavande

NamedTuple宣言の+1。いくつかの変数がある場合、古い方法は本当に不愉快でした...
gebbissimo

@Lavandeマイナーバージョンを1つバックポートする必要があるという、3.6と3.7の間に起こった重大な変更を知っていますか...?
パープルアイス

1
@PurpleIce PEP 557、データクラス@dataclassの実装でした詳細はこちら:pypi.org/project/dataclasses/#description
Lavande

96

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

他のすべてのためには、辞書などのユーティリティクラスを使用することができ、このいずれかを

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

「決定的な」議論は、Pythonクックブックの公開バージョンでここにあると思います。


5
空のクラスでも同じことができるでしょうか?
クルト・劉

44
Pythonを初めて使用する場合は注意してください:C構造体とは異なり、タプルは一度作成すると読み取り専用になります
LeBleu

2
@KurtLiuいいえ、それはおそらく言うでしょうTypeError: this constructor takes no arguments
エフゲニー・セルゲーエフ

84

おそらくあなたはコンストラクタのない構造体を探しています:

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

5
ここで行っていることは技術的には機能します、機能する理由は多くのユーザーにはすぐにはわかりません。下の宣言class Sample:はすぐには何もしません。クラス属性を設定します。これらには、たとえばとしていつでもアクセスできますSample.name
Channing Moore 14年

22
あなたがいる実際に行うと、オブジェクトにインスタンスのプロパティを追加することであるs1s2、実行時に。特に禁止されていない限りname、クラスにname属性があるかどうかにかかわらず、クラスのインスタンスにある属性をいつでも追加または変更できます。おそらくこれを行う際の最大の機能的な問題は、同じクラスの異なるインスタンスが、設定したかどうかに応じて異なる動作をすることですname。更新するとSample.namenameプロパティが明示的に設定されていないオブジェクトは新しいを返しnameます。
Channing Moore 14年

2
これは、構造体-メソッドのない短い「クラス」、デフォルト値のある「フィールド」(クラス属性、私は知っている)に近づくのと同じくらい近いです。変更可能な型(dict、list)でない限り、問題ありません。もちろん、PEP-8やPyCharmの「クラスにinitメソッドがない」のような「フレンドリーな」IDEチェックにぶつかることがあります
Tomasz Gandor

4
Channing Mooreが説明した副作用を実験しました。selfあなたが私に尋ねれば、いくつかのキーワードとコンストラクタラインの経済の価値はありません。Joseが回答を編集して、インスタンス間で誤って値を共有するリスクについての警告メッセージを追加できれば幸いです。
ステファン・C.

@ChanningMoore:あなたが説明していた問題を再現しようとしましたが、失敗しました。問題がポップアップする最小限の実用的な例を提示できますか?
gebbissimo

67

辞書はどうですか?

このようなもの:

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

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

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

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


34
これも私のアプローチですが、辞書はキーとして何でも受け入れられるので、危険だと感じています。myStruct ["field"]を設定するつもりでmyStruct ["ffield"]を設定しても、エラーは発生しません。後でmyStruct ["field"]を使用または再使用するときに、問題が明らかになる場合とそうでない場合があります。私はPabloGのアプローチが好きです。
mobabo 14

同じ問題がPabloGにも存在します。彼に次のコードを追加してみてください:pt3.w = 1 print pt3.w dictsを使用する言語では、特にシリアル化されているオブジェクトに対してそれらを使用することをお勧めします。変なことがない限り、インポートjsonを使用してそれらと他のシリアル化ライブラリを自動的に保存できるためです。辞書の中のもの。Dictは、データとロジックを別々に保つためのソリューションであり、カスタムのシリアライズ関数とシリアライズ解除関数を記述したくない、pickleのような移植性のないシリアライザを使用したくない人にとっては、ストラクチャより優れています。
Poikilos

27

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

マーク:これが欲しいと思ったのは、タプルが欲しいときだけですが、辞書のように「重い」ものはありません。

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

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

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


5
#1、Cython!= CPython。PythonコードをCコードにクロスコンパイルするプロジェクトであるCythonではなく、Cで記述されたPythonの実装であるCPythonについて話していたと思います。私はそれを修正するためにあなたの答えを編集しました。#2、私は彼が口述が重いと言ったとき、彼は構文に言及していたと思います。self['member']はより3文字長くself.member、これらの文字はすべて手首にやさしくありません。
ArtOfWarfare 2015

19

標準ライブラリで利用可能なC構造体をサブクラス化できます。ctypesモジュールが提供構造クラスを。ドキュメントの例:

>>> 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
>>>

18

また、スロットを使用するソリューションを追加したいと思います

class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y

スロットのドキュメントを確実に確認してください。ただし、スロットの簡単な説明は、Pythonの言い方です:「クラスにこれらの属性とこれらの属性のみをロックして、クラスに新しい属性を追加しないことをコミットできる場合インスタンス化されています(はい、クラスインスタンスに新しい属性を追加できます。以下の例を参照してください)。次に、クラスインスタンスに新しい属性を追加できる大きなメモリ割り当てを取り除き、これらのスロットに必要なものだけを使用します属性ます。

クラスインスタンスに属性を追加する例(したがってスロットを使用しない):

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(3,5)
p1.z = 8
print(p1.z)

出力:8

スロットが使用されたクラスインスタンスに属性を追加しようとする例:

class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(3,5)
p1.z = 8

出力:AttributeError: 'Point' object has no attribute 'z'

これは効果的に構造体として機能し、クラスよりも少ないメモリを使用します(構造体のように、私は正確にはどのくらい調べていないが)。オブジェクトのインスタンスを大量に作成し、属性を追加する必要がない場合は、スロットを使用することをお勧めします。ポイントオブジェクトは、データセットを説明するために多くのポイントをインスタンス化する可能性が高いため、この良い例です。


17

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

# 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

7
位置による更新では、属性の宣言順序が無視され、代わりにアルファベット順の並べ替えが使用されます。したがって、Point3dStruct宣言で行の順序を変更するPoint3dStruct(5, 6)と、期待どおりに動作しません。6年間ずっと誰もこれを書いていないのは奇妙です。
ラピス2014年

素晴らしいコードにPython 3バージョンを追加できますか?すごい仕事!私はあなたが抽象的なものを取り、2番目の特定のクラスでそれを明示的にするのが好きです。これは、エラー処理/キャッチに適しています。Python 3の場合、print> print()attrs[n]>を変更するだけですnext(attrs)(filterは独自の反復可能なオブジェクトであり、が必要ですnext)。
ジョナサンコマー

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'])

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


私はpandas.Series(a = 42)を使用します;-)
Mark Horvath

8

PythonでCスタイルの構造体にアクセスするには、次のようにします。

class cstruct:
    var_i = 0
    var_f = 0.0
    var_str = ""

cstructのオブジェクトを使用したいだけの場合

obj = cstruct()
obj.var_i = 50
obj.var_f = 50.00
obj.var_str = "fifty"
print "cstruct: obj i=%d f=%f s=%s" %(obj.var_i, obj.var_f, obj.var_str)

cstructのオブジェクトの配列を作成する場合

obj_array = [cstruct() for i in range(10)]
obj_array[0].var_i = 10
obj_array[0].var_f = 10.00
obj_array[0].var_str = "ten"

#go ahead and fill rest of array instaces of struct

#print all the value
for i in range(10):
    print "cstruct: obj_array i=%d f=%f s=%s" %(obj_array[i].var_i, obj_array[i].var_f, obj_array[i].var_str)

注:「cstruct」名の代わりに、var_i、var_f、var_strの代わりに構造体名を使用してください。構造体のメンバー変数を定義してください。


3
これは、stackoverflow.com / a / 3761729/1877426にあるものと何が違うのですか?
lagweezle

8

ここでの回答のいくつかは非常に複雑です。私が見つけた最も簡単なオプションは(from: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

編集:申し訳ありませんが、この例はすでに下にありません。


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
>>> 

ダムニット-今日私はこれを行うために自分のデコレータを書くのに2時間費やしましたが、これを見つけました。とにかく、私の投稿はデフォルト値を処理しますが、デフォルト値は処理しないためです。stackoverflow.com/a/32448434/901641
ArtOfWarfare 2015

func_codeについて言及する場合は+1。その方向に掘り始めて、そこにたくさんの興味深いものを見つけました。
wombatonfire 2016年

5

渡されたすべての引数またはデフォルトがインスタンスに割り当てられるように、任意のメソッドで使用できるデコレーターを作成しました。

def argumentsToAttributes(method):
    argumentNames = method.func_code.co_varnames[1:]

    # Generate a dictionary of default values:
    defaultsDict = {}
    defaults = method.func_defaults if method.func_defaults else ()
    for i, default in enumerate(defaults, start = len(argumentNames) - len(defaults)):
        defaultsDict[argumentNames[i]] = default

    def newMethod(self, *args, **kwargs):
        # Use the positional arguments.
        for name, value in zip(argumentNames, args):
            setattr(self, name, value)

        # Add the key word arguments. If anything is missing, use the default.
        for name in argumentNames[len(args):]:
            setattr(self, name, kwargs.get(name, defaultsDict[name]))

        # Run whatever else the method needs to do.
        method(self, *args, **kwargs)

    return newMethod

簡単なデモ。位置引数a、のデフォルト値b、および名前付き引数を使用していることに注意してくださいc。次に、3つの参照すべてを印刷してself、メソッドに入る前にそれらが適切に割り当てられていることを示します。

class A(object):
    @argumentsToAttributes
    def __init__(self, a, b = 'Invisible', c = 'Hello'):
        print(self.a)
        print(self.b)
        print(self.c)

A('Why', c = 'Nothing')

私のデコレータはだけでなく、どのメソッドでも機能することに注意してください__init__


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

つまり、空のクラスオブジェクトが作成されてからインスタンス化され、フィールドが動的に追加されます。

これの利点は、本当にシンプルなことです。欠点は、それが特に自己文書化されていないこと(目的のメンバーがクラス "definition"のどこにもリストされていないこと)であり、未設定のフィールドがアクセス時に問題を引き起こす可能性があることです。これら2つの問題は、次の方法で解決できます。

class Employee:
    def __init__ (self):
        self.name = None # or whatever
        self.dept = None
        self.salary = None

これで、一目で、プログラムが期待するフィールドを少なくとも確認できます。

どちらもタイプミスが起こりやすく、john.slarly = 1000成功します。それでも動作します。


4

これは、データを保持するためにクラス(インスタンス化されていない)を使用するソリューションです。この方法はタイピングがほとんど必要なく、追加のパッケージなどを必要としないのが好きです

class myStruct:
    field1 = "one"
    field2 = "2"

必要に応じて、後でフィールドを追加できます。

myStruct.field3 = 3

値を取得するには、フィールドに通常どおりアクセスします。

>>> myStruct.field1
'one'

2

個人的には、このバリアントも好きです。@dFの回答を拡張します

class struct:
    def __init__(self, *sequential, **named):
        fields = dict(zip(sequential, [None]*len(sequential)), **named)
        self.__dict__.update(fields)
    def __repr__(self):
        return str(self.__dict__)

2つの初期化モードをサポートします(ブレンド可能)。

# Struct with field1, field2, field3 that are initialized to None.
mystruct1 = struct("field1", "field2", "field3") 
# Struct with field1, field2, field3 that are initialized according to arguments.
mystruct2 = struct(field1=1, field2=2, field3=3)

また、よりきれいに印刷されます:

print(mystruct2)
# Prints: {'field3': 3, 'field1': 1, 'field2': 2}

2

構造体に対する次の解決策は、namedtupleの実装と以前の回答のいくつかに触発されています。ただし、namedtupleとは異なり、値は変更可能ですが、名前/属性で不変のCスタイルの構造体のように、通常のクラスやdictでは変更できません。

_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'

2

この目的のためのpythonパッケージがあります。cstruct2pyを参照してください

cstruct2pyCコードからPythonクラスを生成し、それらを使用してデータをパックおよびアンパックするための純粋なPythonライブラリです。ライブラリは、Cヘッドレス(構造体、共用体、列挙型、配列宣言)を解析し、それらをPythonでエミュレートできます。生成されたpythonicクラスは、データを解析してパックできます。

例えば:

typedef struct {
  int x;
  int y;
} Point;

after generating pythonic class...
p = Point(x=0x1234, y=0x5678)
p.packed == "\x34\x12\x00\x00\x78\x56\x00\x00"

使い方

最初に、pythonic構造体を生成する必要があります。

import cstruct2py
parser = cstruct2py.c2py.Parser()
parser.parse_file('examples/example.h')

これで、Cコードからすべての名前をインポートできます。

parser.update_globals(globals())

それを直接行うこともできます:

A = parser.parse_string('struct A { int x; int y;};')

Cコードからの型と定義の使用

a = A()
a.x = 45
print a
buf = a.packed
b = A(buf)
print b
c = A('aaaa11112222', 2)
print c
print repr(c)

出力は次のようになります。

{'x':0x2d, 'y':0x0}
{'x':0x2d, 'y':0x0}
{'x':0x31316161, 'y':0x32323131}
A('aa111122', x=0x31316161, y=0x32323131)

クローン

クローンcstruct2py実行の場合:

git clone https://github.com/st0ky/cstruct2py.git --recursive

0

Python構造辞書はこの要件に適していると思います。

d = dict{}
d[field1] = field1
d[field2] = field2
d[field2] = field3

0

https://stackoverflow.com/a/32448434/159695はPython3では機能しません。

https://stackoverflow.com/a/35993/159695はPython3で動作します。

そして、それを拡張してデフォルト値を追加します。

class myStruct:
    def __init__(self, **kwds):
        self.x=0
        self.__dict__.update(kwds) # Must be last to accept assigned member variable.
    def __repr__(self):
        args = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()]
        return '%s(%s)' % ( self.__class__.__qualname__, ', '.join(args) )

a=myStruct()
b=myStruct(x=3,y='test')
c=myStruct(x='str')

>>> a
myStruct(x=0)
>>> b
myStruct(x=3, y='test')
>>> c
myStruct(x='str')

0

@dataclassに3.7がなく、可変性が必要な場合は、次のコードが役立つことがあります。完全に自己文書化されており、IDEと相性がよく(オートコンプリート)、2度書くことを防ぎ、簡単に拡張でき、すべてのインスタンス変数が完全に初期化されていることをテストするのは非常に簡単です。

class Params():
    def __init__(self):
        self.var1 : int = None
        self.var2 : str = None

    def are_all_defined(self):
        for key, value in self.__dict__.items():
            assert (value is not None), "instance variable {} is still None".format(key)
        return True


params = Params()
params.var1 = 2
params.var2 = 'hello'
assert(params.are_all_defined)

0

ここにすばやく汚いトリックがあります:

>>> ms = Warning()
>>> ms.foo = 123
>>> ms.bar = 'akafrit'

どのように機能しますか?組み込みクラスWarning(から派生Exception)を再利用し、独自に定義したクラスと同じように使用します。

良い点は、最初に何かをインポートしたり定義したりする必要がないこと、「警告」は短い名前であること、そしてあなたの小さなスクリプト以外で使用すべきでないダーティなことをしていることも明らかにしていることです。

ちなみに、私はもっと簡単なものを見つけようとしましたが、見つけることms = object()ができませんでした(この最後の例は機能していません)。もしあれば、興味があります。


0

これを行うことがわかった最良の方法は、この投稿で説明されているカスタム辞書クラスを使用することでした:https : //stackoverflow.com/a/14620633/8484485

iPythonオートコンプリートサポートが必要な場合は、dir()関数を次のように定義するだけです。

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self
    def __dir__(self):
        return self.keys()

次に、次のように擬似構造体を定義します:(これはネストされています)

my_struct=AttrDict ({
    'com1':AttrDict ({
        'inst':[0x05],
        'numbytes':2,
        'canpayload':False,
        'payload':None
    })
})

その後、次のようにmy_struct内の値にアクセスできます。

print(my_struct.com1.inst)

=>[5]


0

NamedTupleは快適です。しかし、パフォーマンスとストレージを共有する人はいません。

from typing import NamedTuple
import guppy  # pip install guppy
import timeit


class User:
    def __init__(self, name: str, uid: int):
        self.name = name
        self.uid = uid


class UserSlot:
    __slots__ = ('name', 'uid')

    def __init__(self, name: str, uid: int):
        self.name = name
        self.uid = uid


class UserTuple(NamedTuple):
    # __slots__ = ()  # AttributeError: Cannot overwrite NamedTuple attribute __slots__
    name: str
    uid: int


def get_fn(obj, attr_name: str):
    def get():
        getattr(obj, attr_name)
    return get
if 'memory test':
    obj = [User('Carson', 1) for _ in range(1000000)]      # Cumulative: 189138883
    obj_slot = [UserSlot('Carson', 1) for _ in range(1000000)]          # 77718299  <-- winner
    obj_namedtuple = [UserTuple('Carson', 1) for _ in range(1000000)]   # 85718297
    print(guppy.hpy().heap())  # Run this function individually. 
    """
    Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0 1000000    24 112000000 34 112000000  34 dict of __main__.User
     1 1000000    24 64000000  19 176000000  53 __main__.UserTuple
     2 1000000    24 56000000  17 232000000  70 __main__.User
     3 1000000    24 56000000  17 288000000  87 __main__.UserSlot
     ...
    """

if 'performance test':
    obj = User('Carson', 1)
    obj_slot = UserSlot('Carson', 1)
    obj_tuple = UserTuple('Carson', 1)

    time_normal = min(timeit.repeat(get_fn(obj, 'name'), repeat=20))
    print(time_normal)  # 0.12550550000000005

    time_slot = min(timeit.repeat(get_fn(obj_slot, 'name'), repeat=20))
    print(time_slot)  # 0.1368690000000008

    time_tuple = min(timeit.repeat(get_fn(obj_tuple, 'name'), repeat=20))
    print(time_tuple)  # 0.16006120000000124

    print(time_tuple/time_slot)  # 1.1694481584580898  # The slot is almost 17% faster than NamedTuple on Windows. (Python 3.7.7)

__dict__使用していない場合は、__slots__(より高いパフォーマンスとストレージ)またはNamedTuple(読み取りと使用はクリア)から選択してください

詳細については、 このリンク(スロットの使用法)を確認して__slots__ください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.