Setel atribut dari kamus dengan python

102

Apakah mungkin untuk membuat objek dari kamus dengan python sedemikian rupa sehingga setiap kunci adalah atribut dari objek itu?

Sesuatu seperti ini:

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

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

Saya pikir itu akan menjadi kebalikan dari pertanyaan ini: kamus Python dari bidang objek

OscarRyz
sumber

Jawaban:

172

Tentu, sesuatu seperti ini:

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

Memperbarui

Seperti yang disarankan Brent Nash, Anda dapat membuatnya lebih fleksibel dengan mengizinkan argumen kata kunci juga:

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

Kemudian Anda bisa menyebutnya seperti ini:

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

atau seperti ini:

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

atau bahkan seperti ini:

employee_template = {"role": "minion"}
e = Employee(employee_template, name="abc", age=32)
Ian Clelland
sumber
4
Jika Anda melewatkan data awal karena def __init__(self,**initial_data)Anda mendapatkan keuntungan tambahan karena memiliki metode init yang juga dapat melakukan argumen kata kunci juga (mis. "E = Employee (name = 'Oscar')" atau hanya membaca kamus (mis. "E = Employee ( ** dict) ").
Brent Menulis Kode
2
Menawarkan Employee(some_dict)dan Employee(**some_dict)API tidak konsisten. Mana yang lebih baik harus disediakan.
Mike Graham
4
Jika Anda menetapkan standar arg Anda untuk ()bukan None, Anda bisa melakukannya suka begitu: def __init__(self, iterable=(), **kwargs): self.__dict__.update(iterable, **kwargs).
Matt Anderson
4
Saya tahu ini pertanyaan lama, tapi saya hanya ingin menambahkan bahwa itu bisa dilakukan dalam dua baris dengan pemahaman daftar, misalnya:[[setattr(self,key,d[key]) for key in d] for d in some_dict]
TZ
2
Saya ingin tahu mengapa ini tidak dibuat menjadi python dengan cara yang sederhana. Saya menggunakan ini untuk menangani python GeoIP2 API yang melempar AddressNotFoundError(untuk mengembalikan data yang dikoreksi dalam kasus itu, daripada meledak) - Saya merasa gila bahwa sesuatu yang begitu mudah di PHP ( (object) ['x' => 'y']) membutuhkan begitu banyak cruft dengan Python
Someguy123
46

Menetapkan atribut dengan cara ini hampir pasti bukan cara terbaik untuk memecahkan masalah. Antara:

  1. Anda tahu apa yang harus dilakukan sebelumnya. Dalam hal ini, Anda dapat menyetel semua atribut secara eksplisit. Ini akan terlihat seperti

    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 

    atau

  2. Anda tidak tahu apa yang harus dilakukan sebelumnya. Dalam kasus ini, Anda harus menyimpan data sebagai dict alih-alih mencemari namespace objek. Atribut untuk akses statis. Kasus ini akan terlihat seperti ini

    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 

Solusi lain yang pada dasarnya setara dengan kasus 1 adalah dengan menggunakan a collections.namedtuple. Lihat jawaban van untuk cara mengimplementasikannya.

Mike Graham
sumber
Dan bagaimana jika skenarionya terletak di antara kedua ekstrem Anda? Itulah kasus penggunaan untuk ini, dan saat ini AFAICT tidak ada cara untuk melakukan ini dengan cara KERING dan pythonic.
DylanYoung
15

Anda dapat mengakses atribut suatu objek dengan __dict__, dan memanggil metode pembaruan di atasnya:

>>> 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
Dave Kirby
sumber
3
__dict__adalah artefak implementasi dan tidak boleh digunakan. Juga, ini mengabaikan keberadaan deskriptor di kelas.
Ignacio Vazquez-Abrams
1
@Ignacio apa yang Anda maksud dengan "artefak implementasi"? Apa yang seharusnya tidak kita sadari? Atau mungkin tidak ada di platform yang berbeda? (mis. Python di Windows vs. Python di Linux) Apa jawaban yang dapat diterima?
OscarRyz
12
__dict__adalah bagian bahasa yang terdokumentasi, bukan artefak implementasi.
Dave Kirby
3
Menggunakan setattrlebih disukai daripada mengakses __dict__secara langsung. Anda harus mengingat banyak hal yang dapat menyebabkan Anda __dict__tidak berada di sana atau tidak melakukan apa yang Anda inginkan saat menggunakannya __dict__, tetapi setattrhampir identik dengan benar-benar melakukan foo.bar = baz.
Mike Graham
1
@DaveKirby: Tampaknya penggunaan umum __dict__tidak disarankan: docs.python.org/tutorial/classes.html#id2
equaeghe
11

Mengapa tidak menggunakan nama atribut sebagai kunci kamus?

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

Anda dapat menginisialisasi dengan argumen bernama, daftar tupel, atau kamus, atau penetapan atribut individu, misalnya:

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)

Alternatifnya, alih-alih memunculkan kesalahan atribut, Anda dapat mengembalikan Tidak Ada untuk nilai yang tidak diketahui. (Trik yang digunakan di kelas penyimpanan web2py)

RufusVS
sumber
7

Saya pikir penggunaan jawaban settattradalah cara yang harus dilakukan jika Anda benar-benar membutuhkan dukungan dict.

Tetapi jika Employeeobjek hanyalah sebuah struktur yang dapat Anda akses dengan sintaks titik ( .name) alih-alih sintaks dict ( ['name']), Anda dapat menggunakan namapuple seperti ini:

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

Anda memang memiliki _asdict()metode untuk mengakses semua properti sebagai kamus, tetapi Anda tidak dapat menambahkan atribut tambahan nanti, hanya selama konstruksi.

mobil van
sumber
6

katakanlah misalnya

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

jika Anda ingin mengatur atribut sekaligus

d = {'x':100,'y':300,'z':"blah"}
a = A()
a.__dict__.update(d)
islam ezzat
sumber
1
Anda dapat menggunakan kunci / nilai untuk kenyamanan:a.__dict__.update(x=100, y=300, z="blah")
simno
1

mirip dengan menggunakan dict, Anda bisa menggunakan kwargs seperti ini:

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'
Alex Spencer
sumber