如何创建对象并为其添加属性?


309

我想在Python中创建一个动态对象(在另一个对象内部),然后向其添加属性。

我试过了:

obj = someobject
obj.a = object()
setattr(obj.a, 'somefield', 'somevalue')

但这没用。

有任何想法吗?

编辑:

我正在从for循环遍历值列表的循环中设置属性,例如

params = ['attr1', 'attr2', 'attr3']
obj = someobject
obj.a = object()

for p in params:
   obj.a.p # where p comes from for loop variable

在上面的例子中,我会得到obj.a.attr1obj.a.attr2obj.a.attr3

我使用该setattr函数是因为我不知道如何obj.a.NAMEfor循环中进行操作。

如何根据上例中的值设置属性p


4
你“没用”是什么意思?我认为它引发了AttributeError异常,对吗?
乔什·赖特

1
是的 “对象”对象没有属性“ somefield”
约翰,2010年

6
你为什么做这个?通用的“对象”没有实际意义。您正在创造的东西是什么意思?为什么它不是适当的类或命名元组?
S.Lott

1
这个例子并不小,令我感到困惑,或者我不明白为什么不和一些人合作a = object()而需要obj.a = object()。我再次谈论这个示例,在您的实际代码中,对象内部的对象可能很有用。
kon psych

Answers:


214

您可以使用我的古老的Bunch配方,但是如果您不想创建“绑定类”,那么Python中已经存在一个非常简单的类-所有函数都可以具有任意属性(包括lambda函数)。因此,以下工作:

obj = someobject
obj.a = lambda: None
setattr(obj.a, 'somefield', 'somevalue')

与古老的Bunch食谱相比,清晰度是否还可以,这是一个样式决定,我当然会留给您。


25
正如我提到的,@ FogleBird是一种样式决定。一些受过例如Church的lambda演算训练的CS专家习惯于将函数(lambdas)视为所有数据的基本类型(例如,整数23可以看作等于lambda: 23),因此对于使用lambdas的此类专家而言大概不会感觉像“ hack”。就个人而言,我不太喜欢lambda Python-但这很大程度上是个人喜好问题。
亚历克斯·马蒂利

在某些情况下,考虑lambda模式是否适合您的用例可能会让您认识到,您原本认为是数据的东西实际上实际上更像是一个函数,或者无论如何是函子。
凯尔·斯特兰德

5
@ naught101,在Python中,函数对象,因此您的反对是难以置信的。
亚历克斯·马丁里

6
@ naught101,避免了创建新类型(重用现有类型)的复杂性,它简化了。如今,from argparse import Namespace尽管我希望它可以放在其他地方(例如),但实际上可能更喜欢collection-再次重用一种现存的类型,只是一种更好的类型,而仍然避免使用新类型。但是,那时还不存在:-)。
Alex Martelli 2015年

1
有关类型模块中的SimpleNamespace,请参见下面的“ JF Sebastian”中的答案。如果您的python版本支持它,那么这是最好的解决方案(以及正是SimpleNamespace的目的)
Tim Richardson

333

内置object实例可以实例化,但是不能设置任何属性。(为此,我希望可以。)它没有一个__dict__用于保存属性的属性。

我通常只是这样做:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

如果可以的话,Object根据我要输入的数据类型,给该类一个更有意义的名称。

某些人做不同的事情,他们使用的子类dict允许属性访问获得关键。(d.key代替d['key']

编辑:对于您的问题的补充,使用setattr就可以了。您只是不能setattrobject()实例上使用。

params = ['attr1', 'attr2', 'attr3']
for p in params:
    setattr(obj.a, p, value)

9
可以被实例化,一旦完成就不会用于任何有用的事情。 foo = object()可以,但是您不能做任何事
Daniel DiPaolo 2010年

你好 感谢您的回答。我已经在上面更新了我的问题。看到编辑。你知道答案吗?
约翰

抱歉,我仍然想在对象上进行设置。请参阅上面的更新。
约翰2010年

我真的很喜欢您的回答,我认为将来我会朝这个方向发展。除了这种非常简单,可理解和易读的方法外,我在本文中使用的其他所有内容。使用type....或lambda从来不是我的最爱,就像我的代码中的文本呕吐一样。但是,这种想法非常适合使用对象来保存属性。当我看到lambda时,使代码的可读性更强,使b / c变得更易读,使我的阅读速度降低到25%,而您的方式完全有意义!谢谢。
2016年

很好的答案,我唯一更改的是Struct用作类的名称,以使其更明显。救了我一吨打字[""],干杯!
实用人

137

types.SimpleNamespacePython 3.3+中有一个

obj = someobject
obj.a = SimpleNamespace()
for p in params:
    setattr(obj.a, p, value)
# obj.a.attr1

collections.namedtupletyping.NamedTuple可用于不可变的对象。PEP 557-数据类 建议了一种可变的替代方法。

要获得更丰富的功能,可以尝试使用attrspackage。请参阅用法示例


3
如果您需要使用Python 2.7的功能,也可以尝试使用argparse.Namespace该类
RolKau,2016年

同意-我很好奇这里是否有缺点,但这是Python 3.3+的一个非常方便的功能。
ghukill

该死的!这在2.7上不可用?
Roel

@Roel attrs软件包支持Python 2.7
jfs

对我来说,这似乎比unittest.mock更好。后者有点太重,也更具延展性。对于模拟对象,只需将其赋值给一个属性即可使其变为现实。SimpleNamespace将抵制这种情况。
jdzions

32

有几种方法可以实现此目标。基本上,您需要一个可扩展的对象。

obj.a = type('Test', (object,), {})  
obj.a.b = 'fun'  

obj.b = lambda:None

class Test:
  pass
obj.c = Test()

14
obj.a = type('', (), {})
iman 2014年


21

现在,您可以执行操作(不确定答案是否与罪恶相同):

MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42

@evilpie的答案直接在MyObject(类)上设置属性,而不是像您的实例那样设置属性。
jfs 2015年

18

请尝试以下代码:

$ python
>>> class Container(object):
...     pass 
...
>>> x = Container()
>>> x.a = 10
>>> x.b = 20
>>> x.banana = 100
>>> x.a, x.b, x.banana
(10, 20, 100)
>>> dir(x)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__',     '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'banana']

1
您能解释一下这是什么吗?尽管代码对于解决此问题可能很有用,但对其进行解释不仅可以解决一个问题,而且可以做得更多。
DeadChex

1
@DeadChex显然,它将创建一个新的Class(object),这是一个具有对象属性的空类,并将属性存储在该类中。这甚至比安装更多模块或依靠lambda更好。
m3nda

2
不知道为什么没有更多的投票。是否有理由不将其用于基本容器类?似乎可以在Python
2.7、2.6

17

您也可以直接使用类对象。它创建一个名称空间:

class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')

9

正如文档所说

object__dict__,所以你不能指定任意属性的实例object类。

您可以只使用伪类实例。


2

这些解决方案在测试期间非常有帮助。基于其他人的答案,我在Python 2.7.9中执行此操作(没有静态方法,我得到TypeError(未绑定方法...):

In [11]: auth = type('', (), {})
In [12]: auth.func = staticmethod(lambda i: i * 2)
In [13]: auth.func(2)
Out[13]: 4

1

您正在使用哪些对象?只是尝试了一个示例类,它运行良好:

class MyClass:
  i = 123456
  def f(self):
    return "hello world"

b = MyClass()
b.c = MyClass()
setattr(b.c, 'test', 123)
b.c.test

我得到123了答案。

我看到此失败的唯一情况是,如果您正在尝试setattr对内置对象进行操作。

更新:从注释中可以看出是重复的:为什么不能在python中向对象添加属性?


bc设置为object()而不是已定义的类
约翰,2010年

0

到今天晚了,这是我的一分钱,它的对象恰好在应用程序中保留了一些有用的路径,但是您可以将其适应于任何您希望通过getattr和点表示法访问信息的命令(这是我认为这个问题的真正含义):

import os

def x_path(path_name):
    return getattr(x_path, path_name)

x_path.root = '/home/x'
for name in ['repository', 'caches', 'projects']:
    setattr(x_path, name, os.path.join(x_path.root, name))

这很酷,因为现在:

In [1]: x_path.projects
Out[1]: '/home/x/projects'

In [2]: x_path('caches')
Out[2]: '/home/x/caches'

因此,它像上面的答案一样使用函数对象,但使用函数来获取值(您仍然可以使用,(getattr, x_path, 'repository')而不是x_path('repository')愿意使用)。


0

如果在创建嵌套对象之前可以确定所有属性和值并将它们聚合在一起,那么我们可以创建一个新类,该类在创建时采用字典参数。

# python 2.7

class NestedObject():
    def __init__(self, initial_attrs):
        for key in initial_attrs:
            setattr(self, key, initial_attrs[key])

obj = someobject
attributes = { 'attr1': 'val1', 'attr2': 'val2', 'attr3': 'val3' }
obj.a = NestedObject(attributes)
>>> obj.a.attr1
'val1'
>>> obj.a.attr2
'val2'
>>> obj.a.attr3
'val3'

我们还可以允许关键字参数。看到这篇文章

class NestedObject(object):
    def __init__(self, *initial_attrs, **kwargs):
        for dictionary in initial_attrs:
            for key in dictionary:
                setattr(self, key, dictionary[key])
        for key in kwargs:
            setattr(self, key, kwargs[key])


obj.a = NestedObject(attr1='val1', attr2='val2', attr3= 'val3')

-2
di = {}
for x in range(20):
    name = '_id%s' % x
    di[name] = type(name, (object), {})
    setattr(di[name], "attr", "value")

-2

我看到的其他方式是这样的:

import maya.cmds

def getData(objets=None, attrs=None):
    di = {}
    for obj in objets:
        name = str(obj)
        di[name]=[]
        for at in attrs:
            di[name].append(cmds.getAttr(name+'.'+at)[0])
    return di

acns=cmds.ls('L_vest_*_',type='aimConstraint')
attrs=['offset','aimVector','upVector','worldUpVector']

getData(acns,attrs)

您可以在名称中添加attr这个di [name] .append([at,cmds.getAttr(name +'。'+ at)[0]])
Pablo Emmanuel De Leo

1
这增加了非常大的非标准依赖关系,而简单class a: pass提供了所需的全部功能。
亚历克西斯·帕克斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.