是否有一种方法可以方便地在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
当前回答
下面结构的解决方案是受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'
其他回答
使用命名元组,该元组被添加到Python 2.6标准库中的collections模块中。如果你需要支持Python 2.4,也可以使用Raymond Hettinger的命名元组配方。
它适用于基本示例,但也适用于稍后可能遇到的一些边缘情况。你上面的片段可以写成:
from collections import namedtuple
MyStruct = namedtuple("MyStruct", "field1 field2 field3")
新创建的类型可以这样使用:
m = MyStruct("foo", "bar", "baz")
你也可以使用命名参数:
m = MyStruct(field1="foo", field2="bar", field3="baz")
我在这里没有看到这个答案,所以我想我将添加它,因为我现在正在学习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
也就是说,创建一个空类对象,然后实例化,动态添加字段。
这样做的好处是非常简单。缺点是它不是特别自记录的(在类“定义”中没有列出预期的成员),并且未设置字段在访问时可能会导致问题。这两个问题可以通过以下方法解决:
class Employee:
def __init__ (self):
self.name = None # or whatever
self.dept = None
self.salary = None
现在,您至少可以一目了然地看到程序将期望哪些字段。
两者都很容易打错别字,约翰。Slarly = 1000将成功。不过,它还是有效的。
就我个人而言,我也喜欢这种变体。它扩展了@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}
这里有一个快速而肮脏的技巧:
>>> ms = Warning()
>>> ms.foo = 123
>>> ms.bar = 'akafrit'
它是如何工作的?它只是重用内置类警告(从异常派生),并使用它,因为它是你自己定义的类。
优点是您不需要首先导入或定义任何东西,“警告”是一个简短的名称,并且它还清楚地表明您正在做一些肮脏的事情,不应该在其他地方使用,而应该在您的小脚本中使用。
顺便说一下,我试图找到一些更简单的东西,如ms = object(),但不能(最后一个例子是不工作)。如果你有的话,我很感兴趣。
你可以用元组来做很多你在C语言中使用结构体的事情(比如x,y坐标或RGB颜色)。
对于其他任何东西,你可以使用字典,或像这样的实用程序类:
>>> class Bunch:
... def __init__(self, **kwds):
... self.__dict__.update(kwds)
...
>>> mystruct = Bunch(field1=value1, field2=value2)
我认为“权威”的讨论在这里,在Python Cookbook的出版版本中。