ChatGPT解决这个技术问题 Extra ChatGPT

C-like structures in Python

Is there a way to conveniently define a C-like structure in Python? I'm tired of writing stuff like:

class MyStruct():
    def __init__(self, field1, field2, field3):
        self.field1 = field1
        self.field2 = field2
        self.field3 = field3
Semi-relatedly, algebraic data types would be absolutely wonderful, but to use them well you usually need pattern matching.
Is there anything wrong with this method other than it's tedious to write?
You may find dstruct useful: github.com/dorkitude/dstruct
@levesque harder to re-factor without typos, harder to read at a glance while skimming code, than MyStruct = namedtuple("MyStruct", "field1 field2 field3")
Skip to the 2018 answer: stackoverflow.com/a/45426493/703382

R
Rotareti

Update: Data Classes

With the introduction of Data Classes in Python 3.7 we get very close.

The following example is similar to the NamedTuple example below, but the resulting object is mutable and it allows for default values.

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)

This plays nicely with the new typing module in case you want to use more specific type annotations.

I've been waiting desperately for this! If you ask me, Data Classes and the new NamedTuple declaration, combined with the typing module are a godsend!

Improved NamedTuple declaration

Since Python 3.6 it became quite simple and beautiful (IMHO), as long as you can live with immutability.

A new way of declaring NamedTuples was introduced, which allows for type annotations as well:

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

Mate, you've just made my day - immutable dicts - thank you :D
The dataclass module is new in Python 3.7 but you can pip install dataclasses. It's the backport on Python 3.6. pypi.org/project/dataclasses/#description
+1 for improved NamedTuple declaration. The old way was really unpleasant to read if you had several variables...
@PurpleIce It was an implementation of PEP 557, Data Classes @dataclass The details are here: pypi.org/project/dataclasses/#description
Even with typed fields, you can assign any type to any variable in the class. For example, this works: my_item = MyStruct(123, 123, 123, 123) and every field in my_item will be an integer with value 123. And the same holds for dataclass
g
gz.

Use a named tuple, which was added to the collections module in the standard library in Python 2.6. It's also possible to use Raymond Hettinger's named tuple recipe if you need to support Python 2.4.

It's nice for your basic example, but also covers a bunch of edge cases you might run into later as well. Your fragment above would be written as:

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

The newly created type can be used like this:

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

You can also use named arguments:

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

...but namedtuple is immutable. The example in the OP is mutable.
@mhowison - In my case, that's just a plus.
Nice solution. How would you loop through an array of these tuples? I would assume that fields 1-3 would have to have the same names across tuple objects.
namedtuple can have atmost four arguments so how we can map structure with more data members with corresponding namedtuple
@Kapil - The second argument to namedtuple should be a list of the names of members. That list can be any length.
j
jfs

You can use a tuple for a lot of things where you would use a struct in C (something like x,y coordinates or RGB colors for example).

For everything else you can use dictionary, or a utility class like this one:

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

I think the "definitive" discussion is here, in the published version of the Python Cookbook.


Would a empty class just do the same?
Note if you are new to python: tuples are read-only once created, unlike C structs
@KurtLiu No, it would probably say TypeError: this constructor takes no arguments
This uses an object, with, internally, a dict __dict__ (well, like all objects, except if you use __slots__). So why not use a dict directly? mystruct = {'field1': value1, 'field2': value2}. TL;DR: here you're creating an object just for the purpose of using its internal dict object.__dict__, so it would be less complex to simply use a dict from the beginning.
...especially since you can just do a = dict(foo=123, bar=456) to make that dict if you like the function call syntax with keywords better than the regular dict syntax, and also str()/repr() are somewhat more useful than just giving the object id.
J
Jose M Balaguer

Perhaps you are looking for Structs without constructors:

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

What you're doing here works, technically, but it's probably not immediately apparent to many users why it works. Your declarations under class Sample: don't immediately do anything; they set class attributes. Those can always be accessed as e.g. Sample.name.
What you're actually doing is adding instance properties to the objects s1 and s2 at runtime. Unless otherwise forbidden, you can add or modify the name attribute on any instance of any class at any time, whether or not the class has a name attribute. Probably the biggest functional problem with doing this is that different instances of the same class will behave differently depending on whether you've set name. If you update Sample.name, any objects without an explicitly set name property will return the new name.
This is as close as it gets to a struct - short 'class' with no methods, 'fields' (class attributes, I know) with default values. As long as it's not a mutable type (dict, list), you're fine. Of course, you can hit against PEP-8 or "friendly" IDE checks like PyCharm's "class has no init method".
I experimented the side effect described by Channing Moore. Not worth the economy of a few self keywords and a constructor line if you ask me. I'd appreciate if Jose could edit his answer to add a warning message about the risk of accidentally sharing values accross instances.
@ChanningMoore: I tried to recreate the issue you were describing, but failed. Could you present a minimal working example where the issue pops up?
M
Mark Biek

How about a dictionary?

Something like this:

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

Then you can use this to manipulate values:

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

And the values don't have to be strings. They can be pretty much any other object.


This has been my approach as well, but I feel like it's dangerous precisely because a dictionary can accept anything for a key. There won't be an error if I set myStruct["ffield"] when I meant to set myStruct["field"]. The issue might (or might not) become apparent when I'm using or re-using myStruct["field"] later. I like PabloG's approach.
Same issue exists with PabloG's. Try adding the following code to his: pt3.w = 1 print pt3.w In a language with dicts, it is better to use them, especially for objects being serialized, since you can automatically use import json to save them and other serialization libraries as long as you don't have weird stuff inside of your dict. Dicts are the solution to keeping data and logic separate and are better than structs for people who don't want to write custom serialize and unserialize functions and don't want to use non-portable serializers like pickle.
A
ArtOfWarfare

dF: that's pretty cool... I didn't know that I could access the fields in a class using dict. Mark: the situations that I wish I had this are precisely when I want a tuple but nothing as "heavy" as a dictionary.

You can access the fields of a class using a dictionary because the fields of a class, its methods and all its properties are stored internally using dicts (at least in CPython).

...Which leads us to your second comment. Believing that Python dicts are "heavy" is an extremely non-pythonistic concept. And reading such comments kills my Python Zen. That's not good.

You see, when you declare a class you are actually creating a pretty complex wrapper around a dictionary - so, if anything, you are adding more overhead than by using a simple dictionary. An overhead which, by the way, is meaningless in any case. If you are working on performance critical applications, use C or something.


#1, Cython != CPython. I think you were talking about CPython, the implementation of Python written in C, not Cython, a project to cross compile Python code into C code. I edited your answer to fix that. #2, I think when he said dicts are heavy, he was referring to the syntax. self['member'] is 3 characters longer than self.member, and those characters are all relatively un-wrist-friendly.
R
RagingRoosevelt

I would also like to add a solution that uses slots:

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

Definitely check the documentation for slots but a quick explanation of slots is that it is python's way of saying: "If you can lock these attributes and only these attributes into the class such that you commit that you will not add any new attributes once the class is instantiated (yes you can add new attributes to a class instance, see example below) then I will do away with the large memory allocation that allows for adding new attributes to a class instance and use just what I need for these slotted attributes".

Example of adding attributes to class instance (thus not using slots):

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

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

Output: 8

Example of trying to add attributes to class instance where slots was used:

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

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

Output: AttributeError: 'Point' object has no attribute 'z'

This can effectively works as a struct and uses less memory than a class (like a struct would, although I have not researched exactly how much). It is recommended to use slots if you will be creating a large amount of instances of the object and do not need to add attributes. A point object is a good example of this as it is likely that one may instantiate many points to describe a dataset.


Informative about the slots that are new to me
E
Ella Rose

You can subclass the C structure that is available in the standard library. The ctypes module provides a Structure class. The example from the docs:

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

P
PabloG

You can also pass the init parameters to the instance variables by position

# 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

Updating by position ignores attributes' declaration order and uses their alphabetic sorting instead. So if you change the lines order in Point3dStruct declaration, Point3dStruct(5, 6) won't work as expected. It's strange that nobody has written this in all 6 years.
Could add a Python 3 version to your awesome code? Great work! I like that you take something abstract and make it explicit with the second specific class. That should be good for error handling/catching. For Python 3, just change print > print(), and attrs[n] > next(attrs) (filter now is its own iterable object and requires next).
P
Phlip

Whenever I need an "instant data object that also behaves like a dictionary" (I don't think of C structs!), I think of this cute hack:

class Map(dict):
    def __init__(self, **kwargs):
        super(Map, self).__init__(**kwargs)
        self.__dict__ = self

Now you can just say:

struct = Map(field1='foo', field2='bar', field3=42)

self.assertEquals('bar', struct.field2)
self.assertEquals(42, struct['field3'])

Perfectly handy for those times when you need a "data bag that's NOT a class", and for when namedtuples are incomprehensible...


I use pandas.Series(a=42) ;-)
w
w_jay

Some the answers here are massively elaborate. The simplest option I've found is (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)

Initialising:

>>> options = Struct(answer=42, linelen=80, font='courier')
>>> options.answer
42

adding more:

>>> options.cat = "dog"
>>> options.cat
dog

edit: Sorry didn't see this example already further down.


missing __repr__() method that imo is important
Agreed! Could throw that in, yes.
T
Trevor Hickey

You access C-Style struct in python in following way.

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

if you just want use object of 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)

if you want to create an array of objects of 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)

Note: instead of 'cstruct' name, please use your struct name instead of var_i, var_f, var_str, please define your structure's member variable.


Is this any different than what is in stackoverflow.com/a/3761729/1877426 ?
u
user124757

This might be a bit late but I made a solution using Python Meta-Classes (decorator version below too).

When __init__ is called during run time, it grabs each of the arguments and their value and assigns them as instance variables to your class. This way you can make a struct-like class without having to assign every value manually.

My example has no error checking so it is easier to follow.

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 

Here it is in action.

>>> class MyClass(object):
    __metaclass__ = MyStruct
    def __init__(self, a, b, c):
        pass


>>> my_instance = MyClass(1, 2, 3)
>>> my_instance.a
1
>>> 

I posted it on reddit and /u/matchu posted a decorator version which is cleaner. I'd encourage you to use it unless you want to expand the metaclass version.

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

Damnit - I spent two hours today writing up my own decorator to do this and then I found this. Anyways, posting mine because it handles default values while yours doesn't. stackoverflow.com/a/32448434/901641
+1 for mentioning func_code. Started digging in that direction and found lots of interesting stuff there.
A
ArtOfWarfare

I wrote a decorator which you can use on any method to make it so that all of the arguments passed in, or any defaults, are assigned to the instance.

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 quick demonstration. Note that I use a positional argument a, use the default value for b, and a named argument c. I then print all 3 referencing self, to show that they've been properly assigned before the method is entered.

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

Note that my decorator should work with any method, not just __init__.


J
Jason C

I don't see this answer here, so I figure I'll add it since I'm leaning Python right now and just discovered it. The Python tutorial (Python 2 in this case) gives the following simple and effective example:

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

That is, an empty class object is created, then instantiated, and the fields are added dynamically.

The up-side to this is its really simple. The downside is it isn't particularly self-documenting (the intended members aren't listed anywhere in the class "definition"), and unset fields can cause problems when accessed. Those two problems can be solved by:

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

Now at a glance you can at least see what fields the program will be expecting.

Both are prone to typos, john.slarly = 1000 will succeed. Still, it works.


j
jochen

Here is a solution which uses a class (never instantiated) to hold data. I like that this way involves very little typing and does not require any additional packages etc.

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

You can add more fields later, as needed:

myStruct.field3 = 3

To get the values, the fields are accessed as usual:

>>> myStruct.field1
'one'

It kinda works but does not know how to print itself out myStruct Out[5]: __main__.myStruct
And why wouldn't you instantiate the class? What if one wants to use the same struct for different values? s1 = myStruct; s2 = myStruct; s1.field1 = "two" modifies s2. It depends on the use-case, but I think it's generally better/safer to actually instantiate the struct: s1 myStruct(); s2 = myStruct()
@StephenBoesch Yes, this is a very basic approach. I use this sometimes for ad-hoc scripts because it is simple to type, but for code which needs more functionality, I would use a more full-fledged solution like data classes.
@normanius Sure, if you need to copy your "structs", you need to be more clever and I agree that instantiating can create copies for you, if needed.
n
normanius

Personally, I like this variant too. It extends @dF's answer.

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

It supports two modes of initialization (that can be blended):

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

Also, it prints nicer:

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

ש
שמואל ביאליסטוקי

There is a python package exactly for this purpose. see cstruct2py

cstruct2py is a pure python library for generate python classes from C code and use them to pack and unpack data. The library can parse C headres (structs, unions, enums, and arrays declarations) and emulate them in python. The generated pythonic classes can parse and pack the data.

For example:

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"

How to use

First we need to generate the pythonic structs:

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

Now we can import all names from the C code:

parser.update_globals(globals())

We can also do that directly:

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

Using types and defines from the C code

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

The output will be:

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

Clone

For clone cstruct2py run:

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

c
calandoa

Here is a quick and dirty trick:

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

How does it works? It just re-use the builtin class Warning (derived from Exception) and use it as it was you own defined class.

The good points are that you do not need to import or define anything first, that "Warning" is a short name, and that it also makes clear you are doing something dirty which should not be used elsewhere than a small script of yours.

By the way, I tried to find something even simpler like ms = object() but could not (this last exemple is not working). If you have one, I am interested.


C
Carson

NamedTuple is comfortable. but there no one shares the performance and storage.

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)

If your __dict__ is not using, please choose between __slots__ (higher performance and storage) and NamedTuple (clear for reading and use)

You can review this link(Usage of slots ) to get more __slots__ information.


G
Galaxy

https://stackoverflow.com/a/32448434/159695 does not work in Python3.

https://stackoverflow.com/a/35993/159695 works in Python3.

And I extends it to add default values.

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

Well done including the self.__class__.__qualname__ that is new to me
P
PS1

The following solution to a struct is inspired by the namedtuple implementation and some of the previous answers. However, unlike the namedtuple it is mutable, in it's values, but like the c-style struct immutable in the names/attributes, which a normal class or dict isn't.

_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

Usage:

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'

g
gebbissimo

If you don't have a 3.7 for @dataclass and need mutability, the following code might work for you. It's quite self-documenting and IDE-friendly (auto-complete), prevents writing things twice, is easily extendable and it is very simple to test that all instance variables are completely initialized:

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)

h
halfer

The best way I found to do this was to use a custom dictionary class as explained in this post: https://stackoverflow.com/a/14620633/8484485

If iPython autocompletion support is needed, simply define the dir() function like this:

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

You then define your pseudo struct like so: (this one is nested)

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

You can then access the values inside my_struct like this:

print(my_struct.com1.inst)

=>[5]


Y
Yujun Li

I think Python structure dictionary is suitable for this requirement.

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