是否有一种方法可以方便地在Python中定义类c结构?我厌倦了写这样的东西:
class MyStruct():
def __init__(self, field1, field2, field3):
self.field1 = field1
self.field2 = field2
self.field3 = field3
是否有一种方法可以方便地在Python中定义类c结构?我厌倦了写这样的东西:
class MyStruct():
def __init__(self, field1, field2, field3):
self.field1 = field1
self.field2 = field2
self.field3 = field3
当前回答
如果您没有3.7的@dataclass,并且需要可变性,那么下面的代码可能适合您。它是非常自文档化和ide友好的(自动完成),防止编写两次内容,易于扩展,并且非常简单地测试所有实例变量都被完全初始化:
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)
其他回答
就我个人而言,我也喜欢这种变体。它扩展了@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}
每当我需要一个“行为像字典一样的即时数据对象”(我不会想到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'])
当你需要一个“不是类的数据包”的时候,非常方便,当命名元组是不可理解的……
我发现做到这一点的最好方法是使用自定义字典类,如本文所述: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中的值:
打印(my_struct.com1.inst)
= > [5]
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')
我能想到的最干净的方法是使用一个类装饰器,它可以让你声明一个静态类,并将其重写为一个具有正常命名属性的结构体:
from as_struct import struct
@struct
class Product():
name = 'unknown product'
quantity = -1
sku = '-'
# create instance
p = Product('plush toy', sku='12-345-6789')
# check content:
p.name # plush toy
p.quantity # -1
p.sku # 12-345-6789
使用以下装饰器代码:
def struct(struct_class):
# create a new init
def struct_init(self, *args, **kwargs):
i = 0 # we really don't need enumerate() here...
for value in args:
name = member_names[i]
default_value = member_values[i]
setattr(self, name, value if value is not None else default_value)
i += 1 # ...we just need to inc an int
for key,value in kwargs.items():
i = member_names.index(key)
default_value = member_values[i]
setattr(self, key, value if value is not None else default_value)
# extract the struct members
member_names = []
member_values = []
for attr_name in dir(struct_class):
if not attr_name.startswith('_'):
value = getattr(struct_class, attr_name)
if not callable(value):
member_names.append(attr_name)
member_values.append(value)
# rebind and return
struct_class.init = struct_init
return struct_class
它的工作原理是获取类,提取字段名及其默认值,然后重写类的__init__函数,根据知道哪个参数索引映射到哪个属性名来设置self属性。