简体   繁体   English

Python 中的类 C 结构

[英]C-like structures in Python

Is there a way to conveniently define a C-like structure in Python?有没有办法在Python中方便的定义一个类C的结构体? I'm tired of writing stuff like:我厌倦了写这样的东西:

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

Update : Data Classes更新:数据类

With the introduction of Data Classes in Python 3.7 we get very close.随着Python 3.7数据类的引入,我们已经非常接近了。

The following example is similar to the NamedTuple example below, but the resulting object is mutable and it allows for default values.下面的示例类似于下面的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)

This plays nicely with the new typing module in case you want to use more specific type annotations.如果您想使用更具体的类型注释,这可以很好地与新的输入模块配合使用。

I've been waiting desperately for this!我一直在等这个! If you ask me, Data Classes and the new NamedTuple declaration, combined with the typing module are a godsend!如果你问我,数据类和新的NamedTuple声明,结合打字模块是天赐之物!

Improved NamedTuple declaration改进的 NamedTuple 声明

Since Python 3.6 it became quite simple and beautiful (IMHO), as long as you can live with immutability .Python 3.6 开始,它变得非常简单和漂亮(恕我直言),只要你能忍受不变性

Anew way of declaring NamedTuples was introduced, which allows for type annotations as well:引入了一种声明 NamedTuples 的新方法,它也允许类型注释

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

Use a named tuple , which was added to the collections module in the standard library in Python 2.6.使用命名的 tuple ,它被添加到 Python 2.6 标准库中的collections 模块中。 It's also possible to use Raymond Hettinger's named tuple recipe if you need to support Python 2.4.如果您需要支持 Python 2.4,也可以使用 Raymond Hettinger 的命名元组配方。

It's nice for your basic example, but also covers a bunch of edge cases you might run into later as well.这对您的基本示例很好,但也涵盖了您稍后可能会遇到的一堆边缘情况。 Your fragment above would be written as:您上面的片段将写为:

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

The newly created type can be used like this:新创建的类型可以这样使用:

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

You can also use named arguments:您还可以使用命名参数:

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

You can use a tuple for a lot of things where you would use a struct in C (something like x,y coordinates or RGB colors for example).您可以将元组用于很多在 C 中使用结构的事情(例如 x,y 坐标或 RGB 颜色)。

For everything else you can use dictionary, or a utility class like this one :对于其他所有内容,您可以使用字典或像这样的实用程序类:

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

I think the "definitive" discussion is here , in the published version of the Python Cookbook.我认为“最终”讨论在这里,在 Python Cookbook 的已出版版本中。

Perhaps you are looking for Structs without constructors:也许您正在寻找没有构造函数的结构:

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

How about a dictionary?字典呢?

Something like this:像这样的东西:

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

Then you can use this to manipulate values:然后您可以使用它来操作值:

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

And the values don't have to be strings.并且值不必是字符串。 They can be pretty much any other object.它们几乎可以是任何其他对象。

dF: that's pretty cool... I didn't know that I could access the fields in a class using dict. dF:那太酷了……我不知道我可以使用 dict 访问类中的字段。

Mark: the situations that I wish I had this are precisely when I want a tuple but nothing as "heavy" as a dictionary.马克:我希望我有这个的情况正是当我想要一个元组但没有像字典那么“重”的时候。

You can access the fields of a class using a dictionary because the fields of a class, its methods and all its properties are stored internally using dicts (at least in CPython).您可以使用字典访问类的字段,因为类的字段、它的方法和它的所有属性都是使用 dicts 内部存储的(至少在 CPython 中)。

...Which leads us to your second comment. ...这使我们看到您的第二条评论。 Believing that Python dicts are "heavy" is an extremely non-pythonistic concept.相信 Python dicts 是“沉重的”是一个非常非 Pythonistic 的概念。 And reading such comments kills my Python Zen.阅读这些评论会扼杀我的 Python Zen。 That's not good.这不好。

You see, when you declare a class you are actually creating a pretty complex wrapper around a dictionary - so, if anything, you are adding more overhead than by using a simple dictionary.你看,当你声明一个类时,你实际上是在为字典创建一个非常复杂的包装器——所以,如果有的话,你会增加比使用简单字典更多的开销。 An overhead which, by the way, is meaningless in any case.顺便说一句,在任何情况下都是没有意义的开销。 If you are working on performance critical applications, use C or something.如果您正在开发性能关键的应用程序,请使用 C 或其他东西。

I would also like to add a solution that uses slots :我还想添加一个使用插槽的解决方案:

class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y

Definitely check the documentation for slots but a quick explanation of slots is that it is python's way of saying: "If you can lock these attributes and only these attributes into the class such that you commit that you will not add any new attributes once the class is instantiated (yes you can add new attributes to a class instance, see example below) then I will do away with the large memory allocation that allows for adding new attributes to a class instance and use just what I need for these slotted attributes".一定要检查插槽的文档,但对插槽的快速解释是,这是 Python 的说法:“如果您可以锁定这些属性,并且仅将这些属性锁定到类中,这样您就不会在类中添加任何新属性被实例化(是的,您可以向类实例添加新属性,请参见下面的示例)然后我将取消允许向类实例添加新属性的大内存分配,并仅使用这些插槽属性所需的内容“。

Example of adding attributes to class instance (thus not using slots):向类实例添加属性的示例(因此不使用插槽):

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(3,5)
p1.z = 8
print(p1.z)

Output: 8输出:8

Example of trying to add attributes to class instance where slots was used:尝试向使用插槽的类实例添加属性的示例:

class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(3,5)
p1.z = 8

Output: AttributeError: 'Point' object has no attribute 'z'输出:AttributeError: 'Point' 对象没有属性 'z'

This can effectively works as a struct and uses less memory than a class (like a struct would, although I have not researched exactly how much).这可以有效地用作结构体,并且比类使用更少的内存(就像结构体一样,虽然我还没有研究到底有多少)。 It is recommended to use slots if you will be creating a large amount of instances of the object and do not need to add attributes.如果您将创建对象的大量实例并且不需要添加属性,则建议使用插槽。 A point object is a good example of this as it is likely that one may instantiate many points to describe a dataset.一个点对象就是一个很好的例子,因为一个人可能会实例化许多点来描述一个数据集。

You can subclass the C structure that is available in the standard library.您可以对标准库中可用的 C 结构进行子类化。 The ctypes module provides a Structure class . ctypes模块提供了一个Structure 类 The example from the docs:文档中的示例:

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

You can also pass the init parameters to the instance variables by position您还可以通过位置将 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

Whenever I need an "instant data object that also behaves like a dictionary" (I don't think of C structs!), I think of this cute hack:每当我需要一个“行为也像字典的即时数据对象”(我认为 C 结构体!)时,我会想到这个可爱的 hack:

class Map(dict):
    def __init__(self, **kwargs):
        super(Map, self).__init__(**kwargs)
        self.__dict__ = self

Now you can just say:现在你可以说:

struct = Map(field1='foo', field2='bar', field3=42)

self.assertEquals('bar', struct.field2)
self.assertEquals(42, struct['field3'])

Perfectly handy for those times when you need a "data bag that's NOT a class", and for when namedtuples are incomprehensible...当你需要一个“不是类的数据包”时,以及当命名元组难以理解时,这非常方便......

Some the answers here are massively elaborate.这里的一些答案非常详尽。 The simplest option I've found is (from: http://norvig.com/python-iaq.html ):我发现的最简单的选项是(来自: http : //norvig.com/python-iaq.html ):

class Struct:
    "A structure that can have any fields defined."
    def __init__(self, **entries): self.__dict__.update(entries)

Initialising:初始化:

>>> options = Struct(answer=42, linelen=80, font='courier')
>>> options.answer
42

adding more:添加更多:

>>> options.cat = "dog"
>>> options.cat
dog

edit: Sorry didn't see this example already further down.编辑:抱歉没有看到这个例子已经进一步下降。

You access C-Style struct in python in following way.您可以通过以下方式在 python 中访问 C 样式结构。

class cstruct:
    var_i = 0
    var_f = 0.0
    var_str = ""

if you just want use object of cstruct如果你只想使用 cstruct 的对象

obj = cstruct()
obj.var_i = 50
obj.var_f = 50.00
obj.var_str = "fifty"
print "cstruct: obj i=%d f=%f s=%s" %(obj.var_i, obj.var_f, obj.var_str)

if you want to create an array of objects of cstruct如果你想创建一个 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=%d f=%f s=%s" %(obj_array[i].var_i, obj_array[i].var_f, obj_array[i].var_str)

Note: instead of 'cstruct' name, please use your struct name instead of var_i, var_f, var_str, please define your structure's member variable.注意:不要使用“cstruct”名称,请使用您的结构名称而不是var_i、var_f、var_str,请定义您的结构的成员变量。

This might be a bit late but I made a solution using Python Meta-Classes (decorator version below too).这可能有点晚了,但我使用 Python 元类(下面的装饰器版本)做了一个解决方案。

When __init__ is called during run time, it grabs each of the arguments and their value and assigns them as instance variables to your class.在运行时调用__init__时,它会获取每个参数及其值,并将它们作为实例变量分配给您的类。 This way you can make a struct-like class without having to assign every value manually.通过这种方式,您可以创建一个类似结构的类,而无需手动分配每个值。

My example has no error checking so it is easier to follow.我的示例没有错误检查,因此更容易遵循。

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 

Here it is in action.这是在行动。

>>> class MyClass(object):
    __metaclass__ = MyStruct
    def __init__(self, a, b, c):
        pass


>>> my_instance = MyClass(1, 2, 3)
>>> my_instance.a
1
>>> 

I posted it on reddit and /u/matchu posted a decorator version which is cleaner.在 reddit 上发布了它/u/matchu发布了一个更干净的装饰器版本。 I'd encourage you to use it unless you want to expand the metaclass version.我鼓励您使用它,除非您想扩展元类版本。

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

I wrote a decorator which you can use on any method to make it so that all of the arguments passed in, or any defaults, are assigned to the instance.我写了一个装饰器,你可以在任何方法上使用它来制作它,以便所有传入的参数或任何默认值都分配给实例。

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 quick demonstration.快速演示。 Note that I use a positional argument a , use the default value for b , and a named argument c .请注意,我使用位置参数a ,使用b的默认值和命名参数c I then print all 3 referencing self , to show that they've been properly assigned before the method is entered.然后我打印所有 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')

Note that my decorator should work with any method, not just __init__ .请注意,我的装饰器应该使用任何方法,而不仅仅是__init__

I don't see this answer here, so I figure I'll add it since I'm leaning Python right now and just discovered it.我在这里没有看到这个答案,所以我想我会添加它,因为我现在正在学习 Python 并且刚刚发现它。 The Python tutorial (Python 2 in this case) gives the following simple and effective example: 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

That is, an empty class object is created, then instantiated, and the fields are added dynamically.也就是说,创建一个空的类对象,然后实例化,并动态添加字段。

The up-side to this is its really simple.这样做的好处是它非常简单。 The downside is it isn't particularly self-documenting (the intended members aren't listed anywhere in the class "definition"), and unset fields can cause problems when accessed.缺点是它不是特别自我记录(预期成员未在类“定义”中的任何地方列出),并且未设置的字段在访问时可能会导致问题。 Those two problems can be solved by:这两个问题可以通过以下方式解决:

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

Now at a glance you can at least see what fields the program will be expecting.现在一目了然,您至少可以看到程序需要哪些字段。

Both are prone to typos, john.slarly = 1000 will succeed.两者都容易出现错别字, john.slarly = 1000会成功。 Still, it works.尽管如此,它仍然有效。

Here is a solution which uses a class (never instantiated) to hold data.这是一个使用类(从未实例化)来保存数据的解决方案。 I like that this way involves very little typing and does not require any additional packages etc.我喜欢这种方式只需要很少的打字,不需要任何额外的包等。

class myStruct:
    field1 = "one"
    field2 = "2"

You can add more fields later, as needed:您可以稍后根据需要添加更多字段:

myStruct.field3 = 3

To get the values, the fields are accessed as usual:要获取这些值,可以像往常一样访问这些字段:

>>> myStruct.field1
'one'

Personally, I like this variant too.就个人而言,我也喜欢这种变体。 It extends @dF's answer .它扩展了@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__)

It supports two modes of initialization (that can be blended):它支持两种初始化模式(可以混合):

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

Also, it prints nicer:此外,它打印得更好:

print(mystruct2)
# Prints: {'field3': 3, 'field1': 1, 'field2': 2}

There is a python package exactly for this purpose.有一个专门用于此目的的 python 包。 see cstruct2pycstruct2py

cstruct2py is a pure python library for generate python classes from C code and use them to pack and unpack data. cstruct2py是一个纯 python 库,用于从 C 代码生成 python 类并使用它们来打包cstruct2py包数据。 The library can parse C headres (structs, unions, enums, and arrays declarations) and emulate them in python.该库可以解析 C 头文件(结构、联合、枚举和数组声明)并在 python 中模拟它们。 The generated pythonic classes can parse and pack the data.生成的pythonic类可以解析和打包数据。

For example:例如:

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"

How to use如何使用

First we need to generate the pythonic structs:首先我们需要生成pythonic结构:

import cstruct2py
parser = cstruct2py.c2py.Parser()
parser.parse_file('examples/example.h')

Now we can import all names from the C code:现在我们可以从 C 代码中导入所有名称:

parser.update_globals(globals())

We can also do that directly:我们也可以直接这样做:

A = parser.parse_string('struct A { int x; int y;};')

Using types and defines from the C code使用 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)

The output will be:输出将是:

{'x':0x2d, 'y':0x0}
{'x':0x2d, 'y':0x0}
{'x':0x31316161, 'y':0x32323131}
A('aa111122', x=0x31316161, y=0x32323131)

Clone克隆

For clone cstruct2py run:对于克隆cstruct2py运行:

git clone https://github.com/st0ky/cstruct2py.git --recursive

NamedTuple is comfortable. NamedTuple很舒服。 but there no one shares the performance and storage.但没有人共享性能和存储。

from typing import NamedTuple
import guppy  # pip install guppy
import timeit


class User:
    def __init__(self, name: str, uid: int):
        self.name = name
        self.uid = uid


class UserSlot:
    __slots__ = ('name', 'uid')

    def __init__(self, name: str, uid: int):
        self.name = name
        self.uid = uid


class UserTuple(NamedTuple):
    # __slots__ = ()  # AttributeError: Cannot overwrite NamedTuple attribute __slots__
    name: str
    uid: int


def get_fn(obj, attr_name: str):
    def get():
        getattr(obj, attr_name)
    return get
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)

If your __dict__ is not using, please choose between __slots__ (higher performance and storage) and NamedTuple (clear for reading and use)如果您的__dict__没有使用,请选择__slots__ (更高的性能和存储)和NamedTuple (清晰读取和使用)

You can review this link( Usage of slots ) to get more __slots__ information.您可以查看此链接( 插槽的用法)以获取更多__slots__信息。

The following solution to a struct is inspired by the namedtuple implementation and some of the previous answers.以下结构体解决方案的灵感来自于 namedtuple 实现和之前的一些答案。 However, unlike the namedtuple it is mutable, in it's values, but like the c-style struct immutable in the names/attributes, which a normal class or dict isn't.但是,与namedtuple 不同的是,它的值是可变的,但就像名称/属性中的c 样式结构不可变一样,普通类或字典不是。

_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

Usage:用法:

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'

Here is a quick and dirty trick:这是一个快速而肮脏的技巧:

>>> ms = Warning()
>>> ms.foo = 123
>>> ms.bar = 'akafrit'

How does it works?它是如何工作的? It just re-use the builtin class Warning (derived from Exception ) and use it as it was you own defined class.它只是重新使用内置类Warning (派生自Exception )并将其用作您自己定义的类。

The good points are that you do not need to import or define anything first, that "Warning" is a short name, and that it also makes clear you are doing something dirty which should not be used elsewhere than a small script of yours.好处是你不需要先导入或定义任何东西,“警告”是一个短名称,它也清楚地表明你正在做一些肮脏的事情,除了你的小脚本之外,不应在其他地方使用。

By the way, I tried to find something even simpler like ms = object() but could not (this last exemple is not working).顺便说一句,我试图找到更简单的东西,比如ms = object()但找不到(最后一个例子不起作用)。 If you have one, I am interested.如果你有一个,我有兴趣。

https://stackoverflow.com/a/32448434/159695 does not work in Python3. https://stackoverflow.com/a/32448434/159695在 Python3 中不起作用。

https://stackoverflow.com/a/35993/159695 works in Python3. https://stackoverflow.com/a/35993/159695适用于 Python3。

And I extends it to add default values.我扩展它以添加默认值。

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

If you don't have a 3.7 for @dataclass and need mutability, the following code might work for you.如果您没有 @dataclass 的 3.7 并且需要可变性,那么以下代码可能适合您。 It's quite self-documenting and IDE-friendly (auto-complete), prevents writing things twice, is easily extendable and it is very simple to test that all instance variables are completely initialized:它是完全自文档化和 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)

The best way I found to do this was to use a custom dictionary class as explained in this post: https://stackoverflow.com/a/14620633/8484485我发现这样做的最好方法是使用自定义字典类,如本文所述: https : //stackoverflow.com/a/14620633/8484485

If iPython autocompletion support is needed, simply define the dir () function like this:如果需要 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()

You then define your pseudo struct like so: (this one is nested)然后你像这样定义你的伪结构:(这个是嵌套的)

my_struct=AttrDict ({
    'com1':AttrDict ({
        'inst':[0x05],
        'numbytes':2,
        'canpayload':False,
        'payload':None
    })
})

You can then access the values inside my_struct like this:然后,您可以像这样访问 my_struct 中的值:

print(my_struct.com1.inst)

=> [5] => [5]

I think Python structure dictionary is suitable for this requirement.我认为Python结构字典适合这个需求。

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

The cleanest way I can think of is to use a class decorator that lets you declare a static class and rewrite it to act as a struct with normal, named properties:我能想到的最干净的方法是使用一个 class 装饰器,它可以让你声明一个 static class 并将其重写为具有正常命名属性的结构:

@as_struct
class Product():
    name = 'unknown product'
    sku = '-'
    quantity = -1
    
p = Product('plush toy', '12-345-6789')

p.name     # plush toy
p.sku      # 12-345-6789
p.quantity # -1 

Using the following decorator code:使用以下装饰器代码:

def as_struct(struct_class):
    # create a new init
    def struct_init(self, *args):
        i = 0 # we really don't need enumerate here...
        for value in args:
            name = field_names[i]
            default_value = field_values[i]
            setattr(self, name, value if value is not None else default_value)
            i += 1 # ...we just need to inc an int
            if i == len(args):
                break
    # extract the struct fields
    field_names = []
    field_values = []
    for attr_name in dir(struct_class):
        if not attr_name.startswith('_'):
            value = getattr(struct_class, attr_name)
            if not callable(value):
                field_names.append(attr_name)
                field_values.append(value)
    # rebind and return
    struct_class.__init__ = struct_init
    return struct_class

Which works by taking the class, extracting the field names and their default values, then rewriting the class's __init__ function to set self attributes based on knowing which argument index maps to which property name.它的工作原理是采用 class,提取字段名称及其默认值,然后重写类的__init__ function以根据知道哪个参数索引映射到哪个属性名称来设置self属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM