Python中的类C结构

Python中的类C结构,python,struct,Python,Struct,有没有一种方法可以方便地在Python中定义类似C的结构?我厌倦了写这样的东西: class MyStruct(): def __init__(self, field1, field2, field3): self.field1 = field1 self.field2 = field2 self.field3 = field3 字典怎么样 大概是这样的: myStruct = {'field1': 'some val', 'field2

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

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

字典怎么样

大概是这样的:

myStruct = {'field1': 'some val', 'field2': 'some val'}
m = MyStruct("foo", "bar", "baz")
然后,您可以使用此操作值:

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

而且值不必是字符串。它们几乎可以是任何其他对象。

在许多情况下都可以使用元组,其中使用C中的结构(例如x、y坐标或RGB颜色)

对于其他所有内容,您都可以使用dictionary或类似以下的实用程序类:

我认为“决定性”的讨论是,在Python Cookbook的出版版本中。

使用a,它是在Python 2.6的标准库中添加到的。如果需要支持Python 2.4,也可以使用Raymond Hettinger的方法

这对于您的基本示例很好,但也涵盖了一些您以后可能遇到的边缘情况。上面的片段将写为:

from collections import namedtuple
MyStruct = namedtuple("MyStruct", "field1 field2 field3")
新创建的类型可以这样使用:

myStruct = {'field1': 'some val', 'field2': 'some val'}
m = MyStruct("foo", "bar", "baz")
还可以使用命名参数:

m = MyStruct(field1="foo", field2="bar", field3="baz")
dF:那太酷了。。。我没有 我知道我可以访问中的字段 使用dict的类

马克:我希望我有这样的情况 这正是我想要一个元组的时候 但没有什么比一个更“沉重”了 字典

您可以使用字典访问类的字段,因为类的字段、方法及其所有属性都是使用dicts(至少在CPython中)在内部存储的

…这就引出了你的第二条评论。相信Python dict是“重的”是一个非常非Python主义的概念。阅读这样的评论会扼杀我的Python禅。那不好


你看,当你声明一个类的时候,你实际上是在围绕一个字典创建一个非常复杂的包装器——因此,如果有什么不同的话,你会比使用一个简单的字典增加更多的开销。顺便说一句,这种开销在任何情况下都是毫无意义的。如果您正在处理性能关键型应用程序,请使用C或其他工具。

您还可以按位置将init参数传递给实例变量

# 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

也许您正在寻找没有构造函数的结构:

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
每当我需要一个“行为也像字典一样的即时数据对象”(我不想到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'])

当您需要一个“非类的数据包”时,以及当命名的元组无法理解时,非常方便…

您可以通过以下方式访问python中的C风格结构

class cstruct:
    var_i = 0
    var_f = 0.0
    var_str = ""
如果您只想使用cstruct的对象 如果要创建cstruct的对象数组 注: 请使用您的结构名称,而不是“cstruct”名称
请定义结构的成员变量,而不是var_i、var_f、var_str

这可能有点晚了,但我使用Python元类(下面也是decorator版本)提出了一个解决方案

当在运行时调用
\uuuu init\uuuu
时,它会获取每个参数及其值,并将它们作为实例变量分配给类。通过这种方式,您可以创建一个类似结构的类,而无需手动分配每个值

我的示例没有错误检查,因此更容易理解

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
>>> 
我发布了一个更干净的装饰版本。我鼓励您使用它,除非您想扩展元类版本

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

您可以对标准库中可用的C结构进行子类化。该模块提供了一个新的功能。文档中的示例:

>>> 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
>>>
>>从ctypes导入*
>>>课程点(结构):
...     _字段=[(“x”,c_int),
…(“y”,c_int)]
...
>>>点=点(10,20)
>>>打印点.x,点.y
10 20
>>>点=点(y=5)
>>>打印点.x,点.y
0 5
>>>第(1、2、3)点
回溯(最近一次呼叫最后一次):
文件“”,第1行,是否在中?
ValueError:初始值设定项太多
>>>
>>>类RECT(结构):
...     _字段\=[(“左上角”,点),
…(“右下角”,点)]
...
>>>rc=矩形(点)
>>>打印rc.upperleft.x,rc.upperleft.y
0 5
>>>打印rc.lowerright.x,rc.lowerright.y
0 0
>>>

我编写了一个decorator,您可以在任何方法上使用它,以便将传入的所有参数或任何默认值分配给实例

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

请注意,我的decorator应该使用任何方法,而不仅仅是
\uuuu init\uuuu
我在这里看不到这个答案,所以我想我会添加它,因为我现在正在学习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
也就是说,创建一个空类对象,然后实例化,并动态添加字段

好的一面是它非常简单。缺点是它不是特别的自文档化(在类“定义”的任何地方都没有列出预期的成员),并且未设置的字段在访问时可能会导致问题。这两个问题可以通过以下方式解决:

class Employee:
    def __init__ (self):
        self.name = None # or whatever
        self.dept = None
        self.salary = None
现在,您至少可以一眼看出程序将需要哪些字段


两者都容易出现打字错误,
john.slary=1000
将成功。尽管如此,它仍然可以工作。

我认为Python结构字典适合这个需求

d=dict{}
d[field1]=field1
d[field2]=field2
d[field2]=field3
更新:数据类 随着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)
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')
如果您想使用更具体的类型注释,这可以很好地与新模块配合使用

我一直在拼命地等这个!如果你问我,数据类和新的
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'))
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
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')
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}
_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'
class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(3,5)
p1.z = 8
print(p1.z)
class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(3,5)
p1.z = 8
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"
import cstruct2py
parser = cstruct2py.c2py.Parser()
parser.parse_file('examples/example.h')
parser.update_globals(globals())
A = parser.parse_string('struct A { int x; int y;};')
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)
git clone https://github.com/st0ky/cstruct2py.git --recursive
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)
class myStruct:
    field1 = "one"
    field2 = "2"
myStruct.field3 = 3
>>> myStruct.field1
'one'
>>> ms = Warning()
>>> ms.foo = 123
>>> ms.bar = 'akafrit'
my_struct=AttrDict ({
    'com1':AttrDict ({
        'inst':[0x05],
        'numbytes':2,
        'canpayload':False,
        'payload':None
    })
})
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)