在调查Ruby时,我遇到了这个问题来创建一个类似Struct的类:
Person = Struct.new(:forname, :surname)
person1 = Person.new('John', 'Doe')
puts person1 #<struct Person forname="John", surname="Doe">
这为我提出了一些Python问题。我在Python中编写了这个机制的[非常]基本克隆:
def Struct(*args):
class NewStruct:
def __init__(self):
for arg in args:
self.__dict__[arg] = None
return NewStruct
>>> Person = Struct('forename', 'surname')
>>> person1 = Person()
>>> person2 = Person()
>>> person1.forename, person1.surname = 'John','Doe'
>>> person2.forename, person2.surname = 'Foo','Bar'
>>> person1.forename
'John'
>>> person2.forename
'Foo'
Python中是否已有类似的机制来处理这个问题? (我通常只使用字典)。
我如何获得Struct()
函数来创建正确的__init__()
参数。 (在这种情况下,我想尽可能执行person1 = Person('John', 'Doe')
命名参数:person1 = Person(surname='Doe', forename='John')
我感兴趣的是,即使有更好的Python机制来解决问题2,也要回答问题2。
答案 0 :(得分:16)
如果您使用的是Python 2.6,请尝试使用标准库namedtuple类。
>>> from collections import namedtuple
>>> Person = namedtuple('Person', ('forename', 'surname'))
>>> person1 = Person('John', 'Doe')
>>> person2 = Person(forename='Adam', surname='Monroe')
>>> person1.forename
'John'
>>> person2.surname
'Monroe'
修改:根据评论,有一个backport for earlier versions of Python
答案 1 :(得分:10)
如果您正在运行python&lt; 2.6或想扩展您的课程以执行更多操作,我建议您使用type()
内置功能。这比你的解决方案更有优势,因为__dict__
的设置发生在类创建而不是实例化。它也没有定义__init__
方法,因此如果类因某种原因再次调用__init__
,则不会导致奇怪的行为。例如:
def Struct(*args, **kwargs):
name = kwargs.pop("name", "MyStruct")
kwargs.update(dict((k, None) for k in args))
return type(name, (object,), kwargs)
像这样使用:
>>> MyStruct = Struct("forename", "lastname")
相当于:
class MyStruct(object):
forename = None
lastname = None
虽然这个:
>>> TestStruct = Struct("forename", age=18, name="TestStruct")
相当于:
class TestStruct(object):
forename = None
age = 18
<强>更新强>
此外,您可以编辑此代码,以便非常容易地防止分配除指定变量之外的其他变量。只需更改Struct()工厂即可指定__slots__
。
def Struct(*args, **kwargs):
name = kwargs.pop("name", "MyStruct")
kwargs.update(dict((k, None) for k in args))
kwargs['__slots__'] = kwargs.keys()
return type(name, (object,), kwargs)
答案 2 :(得分:5)
正如其他人所说,在Python 2.6 / 3.x中命名为元组。对于旧版本,我通常使用Stuff类:
class Stuff(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
john = Stuff(forename='John', surname='Doe')
但这并不能保护您免受误导。在ActiveState上还有一个命名元组的配方:
答案 3 :(得分:3)
这是对Cide的回答的跟进(可能只对那些想要深入挖掘的人感兴趣)。
我在使用Cide更新的Struct()定义时遇到了问题,使用了__slots__。问题是返回的类的实例具有只读属性:
>>> MS = Struct('forename','lastname')
>>> m=MS()
>>> m.forename='Jack'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyStruct' object attribute 'forename' is read-only
似乎__slots__在存在相同名称的类属性时阻止实例级属性。我试图通过提供__init__方法来克服这个问题,因此可以在创建对象时设置实例属性:
def Struct1(*args, **kwargs):
def init(self):
for k,v in kwargs.items():
setattr(self, k, v)
name = kwargs.pop("name", "MyStruct")
kwargs.update(dict((k, None) for k in args))
return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})
作为一个净效应,构造的类只能看到__init__方法和__slots__成员,它可以按照需要运行:
>>> MS1 = Struct1('forename','lastname')
>>> m=MS1()
>>> m.forename='Jack'
>>> m.forename
'Jack'
答案 4 :(得分:2)
ThomasH变种的更新:
def Struct(*args, **kwargs):
def init(self, *iargs, **ikwargs):
for k,v in kwargs.items():
setattr(self, k, v)
for i in range(len(iargs)):
setattr(self, args[i], iargs[i])
for k,v in ikwargs.items():
setattr(self, k, v)
name = kwargs.pop("name", "MyStruct")
kwargs.update(dict((k, None) for k in args))
return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})
这允许传递到__init__()
的参数(和命名参数)(没有任何验证 - 似乎很粗糙):
>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>
在namedtuple()
中查看collections.py如何做到这一点。创建该类并将其作为字符串进行扩展并进行评估。还支持酸洗等等。
答案 5 :(得分:1)
>>> from collections import namedtuple
>>> Person = namedtuple("Person", ("forename", "surname"))
>>> john = Person("John", "Doe")
>>> john.forename
'John'
>>> john.surname
'Doe'
答案 6 :(得分:0)
Python软件包esu带来了可以提供几乎相同功能的结构:
from esu import Struct
Customer = Struct(
'Customer',
'name', 'address',
methods={
'greeting': lambda self: "Hello {}".format(self.__dict__['name'])
})
dave = Customer()
dave.name = 'Dave'
dave.greeting() # => Hello Dave