有没有办法在Python中方便地定义类似C的结构?我厌倦了写下这样的东西:
class MyStruct():
def __init__(self, field1, field2, field3):
self.field1 = field1
self.field2 = field2
self.field3 = field3
Run Code Online (Sandbox Code Playgroud)
gz.*_*gz. 330
使用一个已命名的元组,它被添加到Python 2.6标准库中的collections模块中.如果你需要支持Python 2.4,也可以使用Raymond Hettinger的命名元组配方.
它对你的基本示例很好,但也涵盖了一些你可能会在以后遇到的边缘情况.您上面的片段将写为:
from collections import namedtuple
MyStruct = namedtuple("MyStruct", "field1 field2 field3")
Run Code Online (Sandbox Code Playgroud)
新创建的类型可以像这样使用:
m = MyStruct("foo", "bar", "baz")
Run Code Online (Sandbox Code Playgroud)
您还可以使用命名参数:
m = MyStruct(field1="foo", field2="bar", field3="baz")
Run Code Online (Sandbox Code Playgroud)
Rot*_*eti 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)
Run Code Online (Sandbox Code Playgroud)
如果您想使用更具体的类型注释,这可以很好地与新的输入模块一起使用.
我一直在拼命地等待着这个!如果你问我,数据类和新的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'))
Run Code Online (Sandbox Code Playgroud)
dF.*_*dF. 96
您可以在元组中使用元组,在C中使用结构(例如x,y坐标或RGB颜色).
对于其他一切,你可以使用字典,或像这样的实用程序类:
>>> class Bunch:
... def __init__(self, **kwds):
... self.__dict__.update(kwds)
...
>>> mystruct = Bunch(field1=value1, field2=value2)
Run Code Online (Sandbox Code Playgroud)
我认为"权威"讨论就在这里,在Python Cookbook的发布版本中.
小智 82
也许你正在寻找没有构造函数的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
Run Code Online (Sandbox Code Playgroud)
Mar*_*iek 65
字典怎么样?
像这样的东西:
myStruct = {'field1': 'some val', 'field2': 'some val'}
Run Code Online (Sandbox Code Playgroud)
然后你可以用它来操纵值:
print myStruct['field1']
myStruct['field2'] = 'some other values'
Run Code Online (Sandbox Code Playgroud)
并且值不必是字符串.它们几乎可以是任何其他对象.
Vic*_*rti 26
dF:这很酷......我不知道我可以使用dict访问类中的字段.
马克:我希望我拥有这种情况正是因为我想要一个元组,但没有像字典那样"沉重".
您可以使用字典访问类的字段,因为类的字段,方法及其所有属性都是使用dicts在内部存储的(至少在CPython中).
...这引导我们进行第二次评论.相信Python dicts"重"是一个非常非蟒蛇的概念.阅读这些评论会杀死我的Python Zen.这不好.
你看,当你声明一个类时,你实际上是在一个字典周围创建一个非常复杂的包装器 - 所以,如果有的话,你比使用一个简单的字典增加了更多的开销.顺便说一句,在任何情况下都没有意义的开销.如果您正在处理性能关键型应用程序,请使用C或其他东西.
Oam*_*nji 18
我还想添加一个使用插槽的解决方案:
class Point:
__slots__ = ["x", "y"]
def __init__(self, x, y):
self.x = x
self.y = y
Run Code Online (Sandbox Code Playgroud)
绝对检查插槽的文档,但是对插槽的快速解释是python的说法:"如果你可以锁定这些属性,只将这些属性锁定到类中,这样你提交时就不会在类中添加任何新属性实例化(是的,您可以向类实例添加新属性,请参见下面的示例)然后我将取消大内存分配,允许向类实例添加新属性并使用我需要的这些时隙属性".
向类实例添加属性的示例(因此不使用插槽):
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point(3,5)
p1.z = 8
print(p1.z)
Run Code Online (Sandbox Code Playgroud)
输出:8
尝试向使用插槽的类实例添加属性的示例:
class Point:
__slots__ = ["x", "y"]
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point(3,5)
p1.z = 8
Run Code Online (Sandbox Code Playgroud)
输出:AttributeError:'Point'对象没有属性'z'
这可以有效地作为结构使用,并且使用比类更少的内存(就像结构一样,尽管我还没有详细研究过多少).如果要创建大量对象实例并且不需要添加属性,建议使用插槽.点对象就是一个很好的例子,因为人们可能会实例化许多点来描述数据集.
Pab*_*loG 17
您还可以按位置将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
Run Code Online (Sandbox Code Playgroud)
小智 17
您可以子类化标准库中可用的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 "<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
>>>
Run Code Online (Sandbox Code Playgroud)
Phl*_*lip 10
每当我需要一个"也像字典一样的即时数据对象"时(我不会想到C结构!),我想到了这个可爱的黑客:
class Map(dict):
def __init__(self, **kwargs):
super(Map, self).__init__(**kwargs)
self.__dict__ = self
Run Code Online (Sandbox Code Playgroud)
现在你可以说:
struct = Map(field1='foo', field2='bar', field3=42)
self.assertEquals('bar', struct.field2)
self.assertEquals(42, struct['field3'])
Run Code Online (Sandbox Code Playgroud)
当你需要一个"不是一类"的数据包时,以及当命名元素难以理解的那些时候,非常方便...
您可以通过以下方式访问python中的C-Style结构.
class cstruct:
var_i = 0
var_f = 0.0
var_str = ""
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
注意:请使用结构名而不是var_i,var_f,var_str代替'cstruct'名称,请定义结构的成员变量.
这里的一些答案是大量精心制作的.我发现的最简单的选项是(来自:http://norvig.com/python-iaq.html):
class Struct:
"A structure that can have any fields defined."
def __init__(self, **entries): self.__dict__.update(entries)
Run Code Online (Sandbox Code Playgroud)
初始化:
>>> options = Struct(answer=42, linelen=80, font='courier')
>>> options.answer
42
Run Code Online (Sandbox Code Playgroud)
添加更多:
>>> options.cat = "dog"
>>> options.cat
dog
Run Code Online (Sandbox Code Playgroud)
编辑:抱歉没有看到这个例子已经进一步下来了.
这可能有点晚了,但我使用 Python 元类(下面的装饰器版本)做了一个解决方案。
当__init__在运行时调用时,它获取每个参数及其值并将它们作为实例变量分配给您的类。通过这种方式,您可以创建一个类似结构的类,而无需手动分配每个值。
我的示例没有错误检查,因此更容易遵循。
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
Run Code Online (Sandbox Code Playgroud)
这是在行动。
>>> class MyClass(object):
__metaclass__ = MyStruct
def __init__(self, a, b, c):
pass
>>> my_instance = MyClass(1, 2, 3)
>>> my_instance.a
1
>>>
Run Code Online (Sandbox Code Playgroud)
我在 reddit 上发布了它,/u/matchu发布了一个更干净的装饰器版本。我鼓励您使用它,除非您想扩展元类版本。
>>> 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
>>>
Run Code Online (Sandbox Code Playgroud)
我编写了一个装饰器,可以将其用于任何方法,以便将传入的所有参数或任何默认值都分配给实例。
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
Run Code Online (Sandbox Code Playgroud)
快速演示。请注意,我使用位置参数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')
Run Code Online (Sandbox Code Playgroud)
请注意,我的装饰器应使用任何方法,而不仅仅是__init__。
我在这里没有看到这个答案,所以我想我会添加它,因为我现在正在学习 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
Run Code Online (Sandbox Code Playgroud)
也就是说,创建一个空的类对象,然后实例化,并动态添加字段。
这样做的好处是它非常简单。缺点是它不是特别自我记录(预期成员未在类“定义”中的任何地方列出),并且未设置的字段在访问时可能会导致问题。这两个问题可以通过以下方式解决:
class Employee:
def __init__ (self):
self.name = None # or whatever
self.dept = None
self.salary = None
Run Code Online (Sandbox Code Playgroud)
现在一目了然,您至少可以看到程序需要哪些字段。
两者都容易出现错别字,john.slarly = 1000都会成功。尽管如此,它仍然有效。
这是一个使用类(从未实例化)来保存数据的解决方案。我喜欢这种方式只需要很少的打字,不需要任何额外的包等。
class myStruct:
field1 = "one"
field2 = "2"
Run Code Online (Sandbox Code Playgroud)
您可以稍后根据需要添加更多字段:
myStruct.field3 = 3
Run Code Online (Sandbox Code Playgroud)
要获取这些值,可以像往常一样访问这些字段:
>>> myStruct.field1
'one'
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
483421 次 |
| 最近记录: |