ตั้งค่าคุณลักษณะจากพจนานุกรมใน python


102

เป็นไปได้ไหมที่จะสร้างวัตถุจากพจนานุกรมใน python ในลักษณะที่แต่ละคีย์เป็นแอตทริบิวต์ของวัตถุนั้น

สิ่งนี้:

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

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

ฉันคิดว่ามันจะค่อนข้างตรงกันข้ามกับคำถามนี้: พจนานุกรม Python จากช่องของวัตถุ

คำตอบ:


172

แน่นอนสิ่งนี้:

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)

4
หากคุณส่งผ่านข้อมูลเริ่มต้นในขณะที่def __init__(self,**initial_data)คุณได้รับประโยชน์เพิ่มเติมจากการมีวิธีการเริ่มต้นที่สามารถสร้างอาร์กิวเมนต์คำหลักได้เช่นกัน (เช่น "e = Employee (name = 'Oscar')" หรือใช้ในพจนานุกรม (เช่น "e = Employee ( ** dict) ")
Brent Writes Code

2
การเสนอทั้งAPI Employee(some_dict)และEmployee(**some_dict)API ไม่สอดคล้องกัน อันไหนดีกว่าควรจัดหา
Mike Graham

4
ถ้าคุณตั้งค่าเริ่มต้นหาเรื่องของคุณเพื่อ()แทนNone, def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs)คุณสามารถทำมันชอบดังนั้น:
Matt Anderson

4
ฉันรู้ว่ามันเป็นคำถามเก่า แต่ฉันแค่อยากจะเพิ่มว่ามันสามารถทำได้ในสองบรรทัดด้วยความเข้าใจในรายการเช่น:[[setattr(self,key,d[key]) for key in d] for d in some_dict]
TZ

2
ฉันอยากรู้ว่าทำไมสิ่งนี้ถึงไม่ถูกสร้างขึ้นใน python ด้วยวิธีง่ายๆ ฉันใช้สิ่งนี้เพื่อจัดการกับ python GeoIP2 API ที่ขว้างปาAddressNotFoundError(เพื่อส่งคืนข้อมูลที่แก้ไขในกรณีนั้นแทนที่จะระเบิด) - ฉันพบว่ามันบ้ามากที่บางสิ่งที่ง่ายมากใน PHP ( (object) ['x' => 'y']) จำเป็นต้องมีส่วนใหญ่ใน Python
Someguy123

47

การตั้งค่าแอตทริบิวต์ด้วยวิธีนี้แทบจะไม่ใช่วิธีที่ดีที่สุดในการแก้ปัญหา ทั้ง:

  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. คุณไม่รู้ว่าฟิลด์ทั้งหมดควรเป็นอย่างไรก่อนเวลา ในกรณีนี้คุณควรจัดเก็บข้อมูลเป็นคำสั่งแทนที่จะทำให้เนมสเปซของวัตถุเป็นมลพิษ แอตทริบิวต์มีไว้สำหรับการเข้าถึงแบบคงที่ กรณีนี้จะมีลักษณะดังนี้

    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. ดูคำตอบของรถตู้สำหรับวิธีการนำไปใช้


และจะเกิดอะไรขึ้นถ้าสถานการณ์อยู่ระหว่างสองขั้วของคุณ? นั่นเป็นกรณีการใช้งานที่แม่นยำสำหรับสิ่งนี้และในปัจจุบัน AFAICT ไม่มีวิธีใดที่จะทำเช่นนี้ในรูปแบบแห้งและไพโธนิก
DylanYoung

15

คุณสามารถเข้าถึงแอตทริบิวต์ของวัตถุด้วย__dict__และเรียกใช้วิธีการอัปเดตที่มัน:

>>> 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__เป็นสิ่งประดิษฐ์ในการนำไปใช้งานและไม่ควรใช้ นอกจากนี้สิ่งนี้จะไม่สนใจการมีอยู่ของตัวอธิบายในชั้นเรียน
Ignacio Vazquez-Abrams

1
@Ignacio "การใช้งาน Artifact" หมายถึงอะไร? มีอะไรบ้างที่เราไม่ควรระวัง? หรือว่าอาจไม่มีอยู่ในแพลตฟอร์มที่แตกต่างกัน? (เช่น Python ใน Windows เทียบกับ Python บน Linux) คำตอบที่ยอมรับได้คืออะไร?
OscarRyz

12
__dict__เป็นส่วนที่เป็นเอกสารของภาษาไม่ใช่สิ่งประดิษฐ์การใช้งาน
Dave Kirby

3
การใช้setattrจะดีกว่าในการเข้าถึง__dict__โดยตรง คุณจะต้องเก็บไว้ในใจจำนวนมากของสิ่งที่อาจนำไปสู่การ__dict__ไม่ได้อยู่ที่นั่นหรือไม่ทำในสิ่งที่คุณต้องการเมื่อคุณใช้__dict__แต่เป็นจริงเหมือนกับที่ทำจริงsetattr foo.bar = baz
Mike Graham

1
@DaveKirby: ดูเหมือนว่าการใช้งานทั่วไป__dict__จะไม่แนะนำให้ใช้กับ: docs.python.org/tutorial/classes.html#id2
equaeghe

11

ทำไมไม่ใช้ชื่อแอตทริบิวต์เป็นกุญแจสำคัญในพจนานุกรม?

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)

หรืออีกวิธีหนึ่งคือแทนที่จะเพิ่มข้อผิดพลาดของแอตทริบิวต์คุณสามารถคืนค่าไม่มีสำหรับค่าที่ไม่รู้จัก (เคล็ดลับที่ใช้ในคลาสพื้นที่จัดเก็บ web2py)


7

ฉันคิดว่าคำตอบsettattrนั้นเป็นวิธีที่จะไปหากคุณต้องการสนับสนุนdictจริงๆ

แต่ถ้าEmployeeออบเจ็กต์เป็นเพียงโครงสร้างที่คุณสามารถเข้าถึงได้ด้วย dot syntax ( .name) แทนที่จะเป็น dict syntax ( ['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()วิธีการเข้าถึงคุณสมบัติทั้งหมดเป็นพจนานุกรม แต่คุณไม่สามารถเพิ่มแอตทริบิวต์เพิ่มเติมในภายหลังได้เฉพาะในระหว่างการก่อสร้าง


6

พูดเช่น

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)

1
คุณสามารถใช้คีย์ / ค่าเพื่อความสะดวก:a.__dict__.update(x=100, y=300, z="blah")
simno

1

คล้ายกับการใช้ 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'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.