【问题标题】:Set attributes from dictionary in python在python中从字典中设置属性
【发布时间】:2011-01-28 19:13:02
【问题描述】:

是否可以从 python 中的字典中创建一个对象,使得每个键都是该对象的一个​​属性?

类似这样的:

 d = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 }

 e = Employee(d) 
 print e.name # Oscar 
 print e.age + 10 # 42 

我认为这几乎与这个问题相反:Python dictionary from an object's fields

【问题讨论】:

    标签: python dictionary properties attributes initialization


    【解决方案1】:

    当然,是这样的:

    class Employee(object):
        def __init__(self, initial_data):
            for key in initial_data:
                setattr(self, key, initial_data[key])
    

    更新

    正如 Brent Nash 建议的那样,您也可以通过允许关键字参数来使其更加灵活:

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

    那么你可以这样称呼它:

    e = Employee({"name": "abc", "age": 32})
    

    或者像这样:

    e = Employee(name="abc", age=32)
    

    甚至像这样:

    employee_template = {"role": "minion"}
    e = Employee(employee_template, name="abc", age=32)
    

    【讨论】:

    • 如果您将初始数据作为def __init__(self,**initial_data) 传递,您将获得额外的好处,即拥有一个也可以执行关键字参数的 init 方法(例如“e = Employee(name='Oscar')”或只是输入字典(例如“e = Employee(**dict)”)。
    • 同时提供Employee(some_dict)Employee(**some_dict) API 不一致。应该提供哪个更好。
    • 如果您将 arg 的默认值设置为 () 而不是 None,您可以这样做:def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs)
    • 我知道这是个老问题,但我只想补充一点,它可以通过列表理解分两行完成,例如:[[setattr(self,key,d[key]) for key in d] for d in some_dict]
    • 我很想知道为什么它没有以某种简单的方式内置到 python 中。我正在使用它来处理抛出AddressNotFoundError 的python GeoIP2 API(在这种情况下返回更正的数据,而不是炸毁) - 我发现PHP((object) ['x' => 'y'])中如此简单的东西需要所以这很疯狂Python 中的很多东西
    【解决方案2】:

    以这种方式设置属性几乎肯定不是解决问题的最佳方式。要么:

    1. 您提前知道所有字段应该是什么。在这种情况下,您可以显式设置所有属性。这看起来像

      class Employee(object):
          def __init__(self, name, last_name, age):
              self.name = name
              self.last_name = last_name
              self.age = age
      
      d = {'name': 'Oscar', 'last_name': 'Reyes', 'age':32 }
      e = Employee(**d) 
      
      print e.name # Oscar 
      print e.age + 10 # 42 
      

    2. 您不知道所有字段应该提前是什么。在这种情况下,您应该将数据存储为 dict 而不是污染对象命名空间。属性用于静态访问。这种情况看起来像

      class Employee(object):
          def __init__(self, data):
              self.data = data
      
      d = {'name': 'Oscar', 'last_name': 'Reyes', 'age':32 }
      e = Employee(d) 
      
      print e.data['name'] # Oscar 
      print e.data['age'] + 10 # 42 
      

    另一个基本上等同于案例1的解决方案是使用collections.namedtuple。请参阅 van 的答案以了解如何实现。

    【讨论】:

    • 如果场景介于两个极端之间怎么办?这正是它的用例,目前 AFAICT 没有办法以 DRY 和 Pythonic 的方式做到这一点。
    • 值得注意的是,众所周知的 Pandas 库使用属性进行动态访问。
    【解决方案3】:

    你可以用__dict__访问一个对象的属性,然后调用它的update方法:

    >>> class Employee(object):
    ...     def __init__(self, _dict):
    ...         self.__dict__.update(_dict)
    ... 
    
    
    >>> dict = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 }
    
    >>> e = Employee(dict)
    
    >>> e.name
    'Oscar'
    
    >>> e.age
    32
    

    【讨论】:

    • __dict__ 是一个实现工件,不应使用。此外,这忽略了类中描述符的存在。
    • @Ignacio “实现工件”是什么意思?我们不应该意识到什么?或者它可能不会出现在不同的平台上? (例如,Windows 中的 Python 与 Linux 中的 Python)什么是可以接受的答案?
    • __dict__ 是语言的文档部分,而不是实现工件。
    • 使用setattr 优于直接访问__dict__。当您使用__dict__ 时,您必须记住很多可能导致__dict__ 不存在或不做您想做的事情的事情,但是setattr 实际上与实际做foo.bar = baz 相同。
    • @DaveKirby:似乎建议不要普遍使用__dict__docs.python.org/tutorial/classes.html#id2
    【解决方案4】:

    为什么不直接使用属性名作为字典的键?

    class StructMyDict(dict):
    
         def __getattr__(self, name):
             try:
                 return self[name]
             except KeyError as e:
                 raise AttributeError(e)
    
         def __setattr__(self, name, value):
             self[name] = value
    

    您可以使用命名参数、元组列表或字典进行初始化,或者 单独的属性分配,例如:

    nautical = StructMyDict(left = "Port", right = "Starboard") # named args
    
    nautical2 = StructMyDict({"left":"Port","right":"Starboard"}) # dictionary
    
    nautical3 = StructMyDict([("left","Port"),("right","Starboard")]) # tuples list
    
    nautical4 = StructMyDict()  # fields TBD
    nautical4.left = "Port"
    nautical4.right = "Starboard"
    
    for x in [nautical, nautical2, nautical3, nautical4]:
        print "%s <--> %s" % (x.left,x.right)
    

    或者,您可以为未知值返回 None,而不是引发属性错误。 (web2py存储类中使用的一个技巧)

    【讨论】:

      【解决方案5】:

      举个例子

      class A():
          def __init__(self):
              self.x=7
              self.y=8
              self.z="name"
      

      如果你想一次性设置属性

      d = {'x':100,'y':300,'z':"blah"}
      a = A()
      a.__dict__.update(d)
      

      【讨论】:

      • 为方便起见,您可以使用键/值:a.__dict__.update(x=100, y=300, z="blah")
      【解决方案6】:

      如果您确实需要支持dict,我认为使用settattr 的答案是可行的方法。

      但如果 Employee 对象只是一个结构,您可以使用点语法 (.name) 而不是 dict 语法 (['name']) 访问它,您可以像这样使用 namedtuple

      from collections import namedtuple
      
      Employee = namedtuple('Employee', 'name age')
      e = Employee('noname01', 6)
      print e
      #>> Employee(name='noname01', age=6)
      
      # create Employee from dictionary
      d = {'name': 'noname02', 'age': 7}
      e = Employee(**d)
      print e
      #>> Employee(name='noname02', age=7)
      print e._asdict()
      #>> {'age': 7, 'name': 'noname02'}
      

      您确实有_asdict() 方法可以将所有属性作为字典访问,但​​您不能稍后添加其他属性,只能在构造期间添加。

      【讨论】:

        【解决方案7】:

        类似于使用 dict,您可以像这样使用 kwargs:

        class Person:
           def __init__(self, **kwargs):
               self.properties = kwargs
        
           def get_property(self, key):
               return self.properties.get(key, None)
        
           def main():
               timmy = Person(color = 'red')
               print(timmy.get_property('color')) #prints 'red'
        

        【讨论】:

          【解决方案8】:

          如果您不介意使用库:

          pip install domonic
          

          那么你可以这样做:

          from domonic.javascript import Object
          
          class Employee(Object):
              pass
          
          d = { 'name': 'Oscar', 'lastName': 'Reyes', 'age':32 }
          
          e = Employee(d)
          print(e.name)
          print(e['name'])
          print(e)
          
          # {'name': 'Oscar', 'lastName': 'Reyes', 'age': 32}
          
          

          应该按要求行事。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-09-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多