Đặt thuộc tính từ từ điển trong python


102

Có thể tạo một đối tượng từ từ điển trong python theo cách mà mỗi khóa là một thuộc tính của đối tượng đó không?

Một cái gì đó như thế này:

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

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

Tôi nghĩ nó sẽ ngược lại khá nhiều với câu hỏi này: Từ điển Python từ các trường của một đối tượng

Câu trả lời:


172

Chắc chắn, một cái gì đó như thế này:

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

Cập nhật

Như Brent Nash đề xuất, bạn có thể làm cho điều này linh hoạt hơn bằng cách cho phép các đối số từ khóa:

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])

Sau đó, bạn có thể gọi nó như thế này:

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

hoặc như thế này:

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

hoặc thậm chí như thế này:

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

4
Nếu bạn chuyển dữ liệu ban đầu khi def __init__(self,**initial_data)bạn nhận được lợi ích bổ sung của việc có một phương thức init cũng có thể thực hiện các đối số từ khóa (ví dụ: "e = Employee (name = 'Oscar')" hoặc chỉ cần lấy từ điển (ví dụ: "e = Employee ( ** dict) ").
Brent viết mã

2
Cung cấp cả API Employee(some_dict)Employee(**some_dict)API là không nhất quán. Cái nào tốt hơn nên được cung cấp.
Mike Graham

4
Nếu bạn thiết lập mặc định của arg của bạn để ()thay None, bạn có thể làm điều đó như vậy: def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs).
Matt Anderson

4
Tôi biết đó là câu hỏi cũ, nhưng tôi chỉ muốn nói thêm rằng nó có thể được thực hiện trong hai dòng với khả năng hiểu danh sách, ví dụ:[[setattr(self,key,d[key]) for key in d] for d in some_dict]
TZ

2
Tôi rất muốn biết tại sao điều này không được tích hợp vào python theo một cách đơn giản nào đó. Tôi đang sử dụng điều này để đối phó với việc ném API GeoIP2 của python AddressNotFoundError(để trả lại dữ liệu đã sửa trong trường hợp đó, thay vì làm hỏng ) - Tôi thấy thật điên rồ khi một thứ quá dễ dàng trong PHP ( (object) ['x' => 'y']) lại yêu cầu quá nhiều điều trong Python
Someguy123

46

Đặt thuộc tính theo cách này gần như chắc chắn không phải là cách tốt nhất để giải quyết vấn đề. Hoặc:

  1. Bạn biết tất cả các trường nên đi trước thời hạn. Trong trường hợp đó, bạn có thể đặt tất cả các thuộc tính một cách rõ ràng. Điều này sẽ giống như

    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 

    hoặc là

  2. Bạn không biết tất cả các trường phải đi trước thời hạn. Trong trường hợp này, bạn nên lưu trữ dữ liệu dưới dạng chính tả thay vì làm ô nhiễm không gian tên đối tượng. Các thuộc tính dành cho truy cập tĩnh. Trường hợp này sẽ trông như thế nào

    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 

Một giải pháp khác về cơ bản tương đương với trường hợp 1 là sử dụng a collections.namedtuple. Xem câu trả lời của van để biết cách thực hiện điều đó.


Và điều gì sẽ xảy ra nếu viễn cảnh nằm ở đâu đó giữa hai thái cực của bạn? Đó chính xác là trường hợp sử dụng cho việc này, và hiện tại AFAICT không có cách nào để làm điều này một cách KHÔ và khó chịu.
DylanYoung

15

Bạn có thể truy cập các thuộc tính của một đối tượng bằng __dict__và gọi phương thức cập nhật trên nó:

>>> 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

3
__dict__là một tạo tác triển khai và không nên được sử dụng. Ngoài ra, điều này bỏ qua sự tồn tại của các bộ mô tả trên lớp.
Ignacio Vazquez-Abrams

1
@Ignacio ý bạn là gì với "tạo tác triển khai"? Những gì chúng ta không nên nhận thức về nó? Hoặc rằng nó có thể không có mặt ở các nền tảng khác nhau? (ví dụ: Python trong Windows so với Python trên Linux) Câu trả lời chấp nhận được là gì?
OscarRyz

12
__dict__là một phần được lập thành văn bản của ngôn ngữ, không phải là một phần mềm triển khai.
Dave Kirby

3
Sử dụng setattrthích hơn là truy cập __dict__trực tiếp. Bạn phải ghi nhớ rất nhiều điều có thể dẫn đến việc __dict__không ở đó hoặc không làm những gì bạn muốn khi sử dụng __dict__, nhưng setattrhầu như giống hệt với thực tế foo.bar = baz.
Mike Graham

1
@DaveKirby: Có vẻ như việc sử dụng chung __dict__được khuyến cáo chống lại: docs.python.org/tutorial/classes.html#id2
equaeghe

11

Tại sao không chỉ sử dụng tên thuộc tính làm khóa cho từ điển?

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

Bạn có thể khởi tạo với các đối số được đặt tên, danh sách các bộ giá trị hoặc từ điển hoặc các phép gán thuộc tính riêng lẻ, ví dụ:

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)

Ngoài ra, thay vì tăng lỗi thuộc tính, bạn có thể trả về Không có cho các giá trị không xác định. (Một thủ thuật được sử dụng trong lớp lưu trữ web2py)


7

Tôi nghĩ rằng câu trả lời bằng cách sử dụng settattrlà cách để đi nếu bạn thực sự cần hỗ trợ dict.

Nhưng nếu Employeeđối tượng chỉ là một cấu trúc mà bạn có thể truy cập bằng cú pháp dấu chấm ( .name) thay vì cú pháp dict ( ['name']), bạn có thể sử dụng têntuple như sau:

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'}

Bạn có _asdict()phương thức để truy cập tất cả các thuộc tính dưới dạng từ điển, nhưng bạn không thể thêm các thuộc tính bổ sung sau đó, chỉ trong quá trình xây dựng.


6

nói ví dụ

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

nếu bạn muốn đặt các thuộc tính cùng một lúc

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

1
bạn có thể sử dụng khóa / giá trị cho tiện theo dõi:a.__dict__.update(x=100, y=300, z="blah")
simno

1

tương tự như sử dụng một dict, bạn chỉ có thể sử dụng kwargs như vậy:

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'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.