Python中的类C结构

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

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

使用一个已命名的元组 ,它已被添加到Python 2.6标准库中的collections模块中。 如果您需要支持Python 2.4,也可以使用Raymond Hettinger的命名元组配方。

对于你的基本例子来说,这很好,但也包含了一些你可能会遇到的边缘情况。 你上面的片段会写成:

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

新创build的types可以这样使用:

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

你也可以使用命名参数:

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

您可以使用元组来处理C中的一个结构(例如x,y坐标或RGB颜色)。

对于其他任何你可以使用字典,或像这样的工具类:

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

我认为在“Python食谱”的出版版本中,“确定性”讨论就在这里 。

也许你正在寻找没有构造函数的Structs:

 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 

字典怎么样?

像这样的东西:

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

那么你可以使用它来操纵值:

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

而且值不一定是string。 他们可以是任何其他的对象。

dF:这很酷…我不知道我可以使用字典访问类中的字段。

马克:我希望我有这种情况正是我想要一个元组,但没有像字典那样“重”。

您可以使用字典访问类的字段,因为类的字段,方法及其所有属性都使用string(至less在CPython中)进行内部存储。

这导致我们对你的第二个评论。 相信Python字典是“沉重的”,是一个非常非pythonistic的概念。 阅读这些评论杀死了我的Python禅。 这不好。

你看,当你声明一个类的时候,你实际上是在一个字典上创build了一个非常复杂的包装器 – 所以,如果有的话,你会比使用简单的字典增加更多的开销。 顺便说一句,在任何情况下都是毫无意义的。 如果您正在处理关键性能的应用程序,请使用C或其他。

您也可以按位置将initparameter passing给实例variables

 # 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 

您可以inheritance标准库中可用的C结构。 ctypes模块提供了一个Structure类 。 来自文档的示例:

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

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

当你需要一个“不是一个class级的数据包”的时候,这个时候非常方便,而且当名字是不可理解的时候…

您可以通过以下方式访问python中的C-Style结构体。

 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=%df=%fs=%s" %(obj.var_i, obj.var_f, obj.var_str) 

如果你想创build一个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=%df=%fs=%s" %(obj_array[i].var_i, obj_array[i].var_f, obj_array[i].var_str) 

注意:不要使用'cstruct'这个名字,而是使用你的结构名来代替var_i,var_f,var_str,请定义你的结构的成员variables。

我写了一个装饰器,你可以在任何方法上使用它,这样所有传入的参数或者任何默认值都被分配给实例。

 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引用,以显示在input方法之前它们已被正确分配。

 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__

这可能有点晚,但我使用Python Meta-Classes(下面的装饰器版本)做了一个解决scheme。

当运行时调用__init__时,它会抓取每个参数及其值,并将它们作为实例variables分配给您的类。 这样你可以创build一个类似于结构的类而不必手动赋值。

我的例子没有错误检查,所以更容易遵循。

 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发布了一个更清洁的装饰版本。 我鼓励你使用它,除非你想扩展metaclass版本。

 >>> 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) >>> aa 1 >>> 

这里的一些答案是非常详尽的。 我find的最简单的选项是(来自: 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 

编辑:对不起,没有看到这个例子已经进一步下降。

Python 3.6以来,它变得非常简单和美观(恕我直言):

声明它是这样的:( 注意,这允许types注释 )

 from typing import NamedTuple class MyStruct(NamedTuple): my_string: str my_int: int my_list: list my_dict: dict my_foo: Foo 

像这样使用它:

 my_item = MyStruct( my_string='foo', my_int=0, my_list=['bar'], my_dict={'baz': 'qux'}, my_foo=Foo('bar') ) 

或者如果你真的想这样做:

 my_item = MyStruct('foo', 0, ['bar'], {'baz': 'qux'}, Foo('bar')) 

我没有在这里看到这个答案,所以我想我会添加它,因为我现在正在学习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 

也就是说,创build一个空的类对象,然后实例化,并且dynamic添加这些字段。

真正的简单就在于此。 缺点是它不是特别的自我logging(目标成员没有在类别定义中的任何地方列出),而未设置的字段在访问时会导致问题。 这两个问题可以通过以下方式解决:

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

现在你一眼就可以看到程序至less会有哪些字段。

两者都容易出现拼写错误, john.slarly = 1000会成功。 尽pipe如此,它的工作。

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

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

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

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