从字典中创建类实例属性?

我从一个 CSV 导入和获得数据的大致格式

{ 'Field1' : 3000, 'Field2' : 6000, 'RandomField' : 5000 }

字段的名称是动态的。(嗯,它们是动态的,因为它们可能比 Field1和 Field2更多,但是我知道 Field1Field2总是在那里。

我希望能够将这个字典传递到我的类 allMyFields中,以便我能够以属性的形式访问上述数据。

class allMyFields:
# I think I need to include these to allow hinting in Komodo. I think.
self.Field1 = None
self.Field2 = None


def __init__(self,dictionary):
for k,v in dictionary.items():
self.k = v
#of course, this doesn't work. I've ended up doing this instead
#self.data[k] = v
#but it's not the way I want to access the data.


q = { 'Field1' : 3000, 'Field2' : 6000, 'RandomField' : 5000 }
instance = allMyFields(q)
# Ideally I could do this.
print q.Field1

有什么建议吗?至于为什么——我希望能够利用代码提示的优势,并将数据导入到一个名为 data的字典中,因为我一直在这么做,所以这些都不能满足我的要求。

(因为变量名要到运行时才能解析,所以我还是要给 Komodo 一些建议——我认为 self.Field1 = None应该足够了。)

那么,我该如何做我想做的事情呢? 或者我是否正在创建一个设计糟糕的、非蟒蛇的树呢?

91234 次浏览

You can use setattr (be careful though: not every string is a valid attribute name!):

>>> class AllMyFields:
...     def __init__(self, dictionary):
...         for k, v in dictionary.items():
...             setattr(self, k, v)
...
>>> o = AllMyFields({'a': 1, 'b': 2})
>>> o.a
1

Edit: let me explain the difference between the above code and SilentGhost's answer. The above code snippet creates a class of which instance attributes are based on a given dictionary. SilentGhost's code creates a class whose class attributes are based on a given dictionary.

Depending on your specific situation either of these solutions may be more suitable. Do you plain to create one or more class instances? If the answer is one, you may as well skip object creation entirely and only construct the type (and thus go with SilentGhost's answer).

>>> q = { 'Field1' : 3000, 'Field2' : 6000, 'RandomField' : 5000 }
>>> q = type('allMyFields', (object,), q)
>>> q.Field1
3000

docs for type explain well what's going here (see use as a constructor).

edit: in case you need instance variables, the following also works:

>>> a = q()             # first instance
>>> a.Field1
3000
>>> a.Field1 = 1
>>> a.Field1
1
>>> q().Field1           # second instance
3000

You can also use dict.update instead of manually looping over items (and if you're looping, iteritems is better).

class allMyFields(object):
# note: you cannot (and don't have to) use self here
Field1 = None
Field2 = None


def __init__(self, dictionary):
self.__dict__.update(dictionary)


q = { 'Field1' : 3000, 'Field2' : 6000, 'RandomField' : 5000 }
instance = allMyFields(q)


print instance.Field1      # => 3000
print instance.Field2      # => 6000
print instance.RandomField # => 5000

Use setattr for the pretty way. The quick-n-dirty way is to update the instance internal dictionary:

>>> class A(object):
...    pass
...
>>> a = A()
>>> a.__dict__.update({"foo": 1, "bar": 2})
>>> a.foo
1
>>> a.bar
2
>>>

Using named tuples (Python 2.6):

>>> from collections import namedtuple


>>> the_dict = {'Field1': 3, 'Field2': 'b', 'foo': 4.9}
>>> fields = ' '.join(the_dict.keys())
>>> AllMyFields = namedtuple('AllMyFields', fields)
>>> instance = AllMyFields(**the_dict)


>>> print instance.Field1, instance.Field2, instance.foo
3 b 4.9

You could make a subclass of dict which allows attribute lookup for keys:

class AttributeDict(dict):
def __getattr__(self, name):
return self[name]


q = AttributeDict({ 'Field1' : 3000, 'Field2' : 6000, 'RandomField' : 5000 })
print q.Field1
print q.Field2
print q.RandomField

If you try to look up an attribute that dict already has (say keys or get), you'll get that dict class attribute (a method). If the key you ask for doesn't exist on the dict class, then the __getattr__ method will get called and will do your key lookup.

class SomeClass:
def __init__(self,
property1,
property2):
self.property1 = property1
self.property2 = property2




property_dict = {'property1': 'value1',
'property2': 'value2'}
sc = SomeClass(**property_dict)
print(sc.__dict__)

Or you can try this

class AllMyFields:
def __init__(self, field1, field2, random_field):
self.field1 = field1
self.field2 = field2
self.random_field = random_field


@classmethod
def get_instance(cls, d: dict):
return cls(**d)




a = AllMyFields.get_instance({'field1': 3000, 'field2': 6000, 'random_field': 5000})
print(a.field1)

enhanced of sub class of dict

recurrence dict works!

class AttributeDict(dict):
"""https://stackoverflow.com/a/1639632/6494418"""


def __getattr__(self, name):
return self[name] if not isinstance(self[name], dict) \
else AttributeDict(self[name])




if __name__ == '__main__':
d = {"hello": 1, "world": 2, "cat": {"dog": 5}}
d = AttributeDict(d)
print(d.cat)
print(d.cat.dog)
print(d.cat.items())


"""
{'dog': 5}
5
dict_items([('dog', 5)])
"""

If you are open for adding a new library, pydantic is a very efficient solution. It uses python annotation to construct object and validate type Consider the following code:

from pydantic import BaseModel


class Person(BaseModel):
name: str
age: str




data = {"name": "ahmed", "age": 36}


p = Person(**data)

pydantic: https://pydantic-docs.helpmanual.io/

A simple solution is

field_dict = { 'Field1' : 3000, 'Field2' : 6000, 'RandomField' : 5000 }


# Using dataclasses
from dataclasses import make_dataclass
field_obj = make_dataclass("FieldData", list(field_dict.keys()))(*field_dict.values())


# Using attrs
from attrs import make_class
field_obj = make_class("FieldData", list(field_dict.keys()))(*field_dict.values())