是否有一种方法可以方便地在Python中定义类c结构?我厌倦了写这样的东西:

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

当前回答

下面结构的解决方案是受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'

其他回答

一本字典怎么样?

就像这样:

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

然后你可以使用这个来操作值:

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

值不一定是字符串。它们可以是其他任何物体。

每当我需要一个“行为像字典一样的即时数据对象”(我不会想到C结构体!),我就会想到这个可爱的hack:

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

当你需要一个“不是类的数据包”的时候,非常方便,当命名元组是不可理解的……

有一个python包正是用于此目的。看到cstruct2py

cstruct2py是一个纯python库,用于从C代码生成python类,并使用它们来打包和解包数据。该库可以解析C头文件(结构体、联合、枚举和数组声明),并在python中模拟它们。生成的python类可以解析和打包数据。

例如:

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"

如何使用

首先,我们需要生成python结构体:

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

就我个人而言,我也喜欢这种变体。它扩展了@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__)

它支持两种初始化模式(可以混合使用):

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

dF:太酷了……我没有 我知道我可以访问的领域 一个使用字典的类。 马克:我希望我遇到的情况 这正是我需要一个元组的时候 但没有什么比 字典。

你可以使用字典访问类的字段,因为类的字段、它的方法和它的所有属性都是用字典存储在内部的(至少在CPython中是这样)。

...这就引出了你的第二个评论。相信Python字典是“沉重的”是一个非常非Python主义的概念。读这样的评论简直要了我的Python禅。这可不太好。

您可以看到,当您声明一个类时,实际上是在为一个字典创建一个相当复杂的包装器——因此,如果有的话,您比使用一个简单的字典增加了更多的开销。顺便说一下,这种开销在任何情况下都是没有意义的。如果您正在处理性能关键的应用程序,请使用C或其他语言。