当前位置:  开发笔记 > 编程语言 > 正文

Python中的类C结构

如何解决《Python中的类C结构》经验,为你挑选了13个好方法。

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

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

gz... 330

使用一个已命名的元组,它被添加到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")

...但是,namedtuple是不可变的.OP中的示例是可变的. (158认同)

@mhowison - 就我而言,这只是一个加分. (25认同)

好的解决方案 你将如何遍历这些元组的数组?我假设字段1-3必须在元组对象中具有相同的名称. (3认同)

@Kapil - namedtuple的第二个参数应该是成员名称的列表.该列表可以是任何长度. (3认同)

namedtuple最多可以有四个参数,因此我们如何映射具有更多具有相应namedtuple的数据成员的结构 (2认同)


Rotareti.. 169

更新:数据类

通过引入数据类的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)

如果您想使用更具体的类型注释,这可以很好地与新的输入模块一起使用.

我一直在拼命地等待着这个!如果你问我,数据类和新的NamedTuple声明,结合打字模块是天赐之物!

改进了NamedTuple声明

Python 3.6以来它变得非常简单和美丽(恕我直言),只要你能够忍受不变性.

引入了一种声明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'))

`dataclass`模块是Python 3.7中的新功能,但你可以`pip install dataclasses`.它是Python 3.6的后端.https://pypi.org/project/dataclasses/#description (8认同)

伙计,你刚刚结束了我的一天 - 不可改变的决定 - 谢谢你:D (4认同)


dF... 96

您可以在元组中使用元组,在C中使用结构(例如x,y坐标或RGB颜色).

对于其他一切,你可以使用字典,或像这样的实用程序类:

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

我认为"权威"讨论就在这里,在Python Cookbook的发布版本中.



1> gz...:

使用一个已命名的元组,它被添加到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")


...但是,namedtuple是不可变的.OP中的示例是可变的.
@mhowison - 就我而言,这只是一个加分.
好的解决方案 你将如何遍历这些元组的数组?我假设字段1-3必须在元组对象中具有相同的名称.
@Kapil - namedtuple的第二个参数应该是成员名称的列表.该列表可以是任何长度.
namedtuple最多可以有四个参数,因此我们如何映射具有更多具有相应namedtuple的数据成员的结构

2> Rotareti..:

更新:数据类

通过引入数据类的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)

如果您想使用更具体的类型注释,这可以很好地与新的输入模块一起使用.

我一直在拼命地等待着这个!如果你问我,数据类和新的NamedTuple声明,结合打字模块是天赐之物!

改进了NamedTuple声明

Python 3.6以来它变得非常简单和美丽(恕我直言),只要你能够忍受不变性.

引入了一种声明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'))


`dataclass`模块是Python 3.7中的新功能,但你可以`pip install dataclasses`.它是Python 3.6的后端.https://pypi.org/project/dataclasses/#description
伙计,你刚刚结束了我的一天 - 不可改变的决定 - 谢谢你:D

3> dF...:

您可以在元组中使用元组,在C中使用结构(例如x,y坐标或RGB颜色).

对于其他一切,你可以使用字典,或像这样的实用程序类:

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

我认为"权威"讨论就在这里,在Python Cookbook的发布版本中.


请注意,如果您是python的新手:元组一旦创建就是只读的,与C结构不同
一个空课会做同样的事吗?
@KurtLiu不,它可能会说'TypeError:这个构造函数不带参数`

4> 小智..:

也许你正在寻找没有构造函数的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


你_actually_做的是在运行时向对象`s1`和`s2`添加实例属性.除非另有禁止,否则您可以随时在任何类的任何实例上添加或修改`name`属性,无论该类是否具有`name`属性.这样做的最大功能问题可能是同一个类的不同实例的行为会有所不同,具体取决于你是否设置了`name`.如果更新`Sample.name`,任何没有显式设置`name`属性的对象都将返回新的`name`.
从技术上讲,你在这里所做的工作是有效的,但对许多用户来说可能并不是很明显_Why_它的工作原理.您在`class Sample:`下的声明不会立即执行任何操作; 他们设置了类属性.这些可以随时访问,例如`Sample.name`.
我试验了Channing Moore描述的副作用.如果你问我,不值得一些`self`关键字和构造函数行的经济性.如果Jose可以编辑他的答案,我会很感激,他会添加一条关于在实例中意外分享价值的风险的警告信息.
这与结构很接近 - 没有方法的短'class','fields'(类属性,我知道)有默认值.只要它不是一个可变类型(字典,列表),你就没事了.当然,你可以点击PEP-8或"友好"的IDE检查,比如PyCharm的"类没有__init__方法".

5> Mark Biek..:

字典怎么样?

像这样的东西:

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

然后你可以用它来操纵值:

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

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


这也是我的方法,但我觉得这很危险,因为字典可以接受任何关键字.如果我在设置myStruct ["field"]时设置myStruct ["ffield"],则不会出现错误.当我稍后使用或重新使用myStruct ["field"]时,问题可能(或可能不会)变得明显.我喜欢PabloG的方法.

6> Vicent Marti..:

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

马克:我希望我拥有这种情况正是因为我想要一个元组,但没有像字典那样"沉重".

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

...这引导我们进行第二次评论.相信Python dicts"重"是一个非常非蟒蛇的概念.阅读这些评论会杀死我的Python Zen.这不好.

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


#1,Cython!= CPython.我想你在谈论CPython,用C语言编写的Python的实现,而不是Cython,一个将Python代码交叉编译成C代码的项目.我编辑了你的答案以解决这个问题.#2,我想当他说dicts很重时,他指的是语法.`self ['member']`比'self.member`长3个字符,而且这些字符都是相对不戴手腕的.

7> Oamar Kanji..:

我还想添加一个使用插槽的解决方案:

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

绝对检查插槽的文档,但是对插槽的快速解释是python的说法:"如果你可以锁定这些属性,只将这些属性锁定到类中,这样你提交时就不会在类中添加任何新属性实例化(是的,您可以向类实例添加新属性,请参见下面的示例)然后我将取消大内存分配,允许向类实例添加新属性并使用我需要的这些时隙属性".

向类实例添加属性的示例(因此不使用插槽):

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

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

输出:8

尝试向使用插槽的类实例添加属性的示例:

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

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

输出:AttributeError:'Point'对象没有属性'z'

这可以有效地作为结构使用,并且使用比类更少的内存(就像结构一样,尽管我还没有详细研究过多少).如果要创建大量对象实例并且不需要添加属性,建议使用插槽.点对象就是一个很好的例子,因为人们可能会实例化许多点来描述数据集.



8> PabloG..:

您还可以按位置将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


按位置更新会忽略属性的声明顺序,而是使用字母排序.因此,如果您更改`Point3dStruct`声明中的行顺序,`Point3dStruct(5,6)`将无法按预期工作.奇怪的是,没有人在这6年里写过这篇文章.

9> 小智..:

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

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



10> Phlip..:

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

当你需要一个"不是一类"的数据包时,以及当命名元素难以理解的那些时候,非常方便...



11> Sujal Sheth..:

您可以通过以下方式访问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=%d f=%f s=%s" %(obj.var_i, obj.var_f, obj.var_str)
如果要创建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)

注意:请使用结构名而不是var_i,var_f,var_str代替'cstruct'名称,请定义结构的成员变量.


这有什么不同于http://stackoverflow.com/a/3761729/1877426中的内容吗?

12> w_jay..:

这里的一些答案是大量精心制作的.我发现的最简单的选项是(来自: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

编辑:抱歉没有看到这个例子已经进一步下来了.



13> ArtOfWarfare..:

我编写了一个装饰器,可以将其用于任何方法,以便将传入的所有参数或任何默认值都分配给实例。

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。然后self,我打印所有3个引用,以显示在输入方法之前已正确分配了它们。

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__

推荐阅读
可爱的天使keven_464
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有