Bagaimana saya bisa membuat objek dan menambahkan atribut ke sana?

311

Saya ingin membuat objek dinamis (di dalam objek lain) dengan Python dan kemudian menambahkan atribut padanya.

Saya mencoba:

obj = someobject
obj.a = object()
setattr(obj.a, 'somefield', 'somevalue')

tapi ini tidak berhasil.

Ada ide?

edit:

Saya mengatur atribut dari forloop yang loop melalui daftar nilai, misalnya

params = ['attr1', 'attr2', 'attr3']
obj = someobject
obj.a = object()

for p in params:
   obj.a.p # where p comes from for loop variable

Dalam contoh di atas saya akan mendapatkan obj.a.attr1, obj.a.attr2, obj.a.attr3.

Saya menggunakan setattrfungsi ini karena saya tidak tahu bagaimana melakukannya obj.a.NAMEdari satu forlingkaran.

Bagaimana saya mengatur atribut berdasarkan nilai ppada contoh di atas?

John
sumber
4
Apa yang Anda maksud dengan "tidak berhasil"? Saya menganggap itu menimbulkan pengecualian AttributeError, kan?
Josh Wright
1
ya. 'object' object tidak memiliki atribut 'somefield'
John
6
Mengapa kau melakukan ini? "Objek" generik tidak memiliki arti sebenarnya . Apa arti dari hal yang Anda buat? Mengapa ini bukan kelas yang tepat atau yang bernama Tuple?
S.Lott
1
Contohnya tidak minimal dan membingungkan bagi saya atau saya hanya tidak melihat mengapa Anda tidak bekerja dengan beberapa a = object()dan Anda perlu obj.a = object(). Sekali lagi saya berbicara tentang contohnya, dalam kode aktual Anda sebuah objek di dalam objek mungkin berguna.
kon psych

Jawaban:

215

Anda bisa menggunakan resep Bunch kuno saya , tetapi jika Anda tidak ingin membuat "kelas banyak", yang sangat sederhana sudah ada di Python - semua fungsi dapat memiliki atribut yang berubah-ubah (termasuk fungsi lambda). Jadi, karya-karya berikut:

obj = someobject
obj.a = lambda: None
setattr(obj.a, 'somefield', 'somevalue')

Apakah kehilangan kejelasan dibandingkan dengan Bunchresep terhormat itu baik-baik saja, adalah keputusan gaya tentu saja akan saya serahkan kepada Anda.

Alex Martelli
sumber
25
@FogleBird, keputusan gaya, seperti yang saya sebutkan. Beberapa pakar CS yang dilatih misalnya dalam kalkulus lambda Gereja digunakan untuk memikirkan fungsi (lambdas) sebagai tipe dasar dari semua data (bilangan bulat 23, misalnya, dapat dilihat setara dengan lambda: 23), sehingga untuk para pakar seperti itu menggunakan lambdas untuk tujuan ini mungkin akan terasa seperti "hack". Secara pribadi, saya tidak terlalu suka lambda dengan Python - tapi itu masalah pribadi.
Alex Martelli
Dan dalam beberapa kasus, mempertimbangkan apakah lambdapola cocok untuk kasus penggunaan Anda atau tidak dapat mengarahkan Anda untuk menyadari bahwa sesuatu yang awalnya Anda anggap sebagai data sebenarnya lebih mirip fungsi - atau, dalam hal apa pun, functor.
Kyle Strand
5
@ naught101, fungsi adalah objek, dengan Python, jadi keberatan Anda tidak terduga.
Alex Martelli
6
@ naught101, menghindari pembuatan tipe baru (menggunakan kembali yang sudah ada) tidak menyulitkan, itu menyederhanakan. Saat ini mungkin sebenarnya lebih suka from argparse import Namespacemeskipun saya berharap itu tinggal di tempat lain * misalnya, collection) - lagi menggunakan kembali jenis yang sudah ada, hanya yang lebih baik, dan masih menghindari penciptaan jenis baru. Tapi, itu tidak ada di sana :-).
Alex Martelli
1
Lihat jawaban di bawah ini dari "JF Sebastian" tentang SimpleNamespace dari modul types. Jika versi python Anda mendukungnya, ini adalah solusi terbaik (dan persis seperti apa SimpleNamespace dirancang untuk)
Tim Richardson
334

Built-in objectdapat dipakai tapi tidak dapat memiliki atribut yang ditetapkan di dalamnya. (Saya berharap itu bisa, untuk tujuan yang tepat ini.) Ia tidak perlu __dict__memiliki atribut.

Saya biasanya hanya melakukan ini:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

Ketika saya bisa, saya memberi Objectkelas nama yang lebih bermakna, tergantung pada data apa yang saya masukkan.

Beberapa orang melakukan hal yang berbeda, di mana mereka menggunakan sub-kelas dictyang memungkinkan akses atribut untuk mendapatkan kunci. ( d.keybukannya d['key'])

Sunting : Untuk tambahan pertanyaan Anda, menggunakan setattrtidak apa-apa. Anda tidak bisa menggunakan setattrpada object()contoh.

params = ['attr1', 'attr2', 'attr3']
for p in params:
    setattr(obj.a, p, value)
FogleBird
sumber
9
itu bisa dipakai, hanya tidak digunakan untuk hal-hal yang berguna setelah dilakukan. foo = object()bekerja, tetapi Anda tidak bisa melakukan banyak hal dengan itu
Daniel DiPaolo
Hai. Terima kasih atas jawabannya. Saya telah memperbarui masalah saya di atas. lihat hasil edit. Anda tahu jawabannya?
John
maaf saya masih ingin mengaturnya pada objek. lihat pembaruan di atas.
John
Saya sangat suka jawaban Anda dan saya pikir saya akan condong ke arah ini di masa depan. Saya telah menggunakan semua hal lain di posting ini kecuali untuk metodologi yang sangat sederhana, mudah dimengerti, dan dapat dibaca ini. Menggunakan type....atau lambda tidak pernah menjadi favorit saya, seperti teks muntah dalam kode saya. Tapi ide ini bagus untuk menggunakan objek untuk menyimpan properti. Meninggalkan kode lebih dapat dibaca b / c ketika saya melihat lambda Saya memperlambat pembacaan saya hingga 25% sementara cara Anda masuk akal! Terima kasih.
Marc
jawaban yang bagus, satu-satunya hal yang saya ubah adalah menggunakan Structnama kelas untuk membuatnya lebih jelas. Menghemat saya mengetik ["dan "], Cheers!
pragman
137

Ada types.SimpleNamespacekelas di Python 3.3+ :

obj = someobject
obj.a = SimpleNamespace()
for p in params:
    setattr(obj.a, p, value)
# obj.a.attr1

collections.namedtuple, typing.NamedTuplebisa digunakan untuk benda yang tidak bisa diubah. PEP 557 - Kelas Data menyarankan alternatif yang bisa berubah.

Untuk fungsionalitas yang lebih kaya, Anda bisa mencoba attrspaket . Lihat contoh penggunaan .

jfs
sumber
3
Jika Anda membutuhkan sesuatu yang berfungsi dengan Python 2.7, Anda juga dapat mencoba argparse.Namespacekelas
RolKau
Setuju - Saya ingin tahu apakah ada kerugian di sini, tapi ini adalah python 3.3+ keterjangkauan yang sangat berguna.
ghukill
Sial! ini tidak tersedia pada 2.7?
Roel
attrsPaket @Roel mendukung Python 2.7
jfs
Ini sepertinya solusi yang lebih baik bagi saya daripada unittest.mock; yang terakhir ini agak terlalu berat dan sedikit lebih lunak. Dengan objek tiruan, hanya menetapkan ke atribut akan membuatnya muncul; SimpleNamespace akan menolak itu.
jdzions
32

Ada beberapa cara untuk mencapai tujuan ini. Pada dasarnya Anda membutuhkan objek yang dapat diperpanjang.

obj.a = type('Test', (object,), {})  
obj.a.b = 'fun'  

obj.b = lambda:None

class Test:
  pass
obj.c = Test()
orang jahat
sumber
14
obj.a = type('', (), {})
iman
27

The mockmodul pada dasarnya dibuat untuk itu.

import mock
obj = mock.Mock()
obj.a = 5
Dunatotatos
sumber
3
Kerugiannya adalah itu ketergantungan eksternal
Kangur
6
unittest.Mockadalah bagian dari perpustakaan standar sejak Python 3.3 ( docs.python.org/3/library/unittest.mock.html )
illagrenan
2
Tergantung pada penggunaan kode Anda, saya pikir. Jika itu kode produksi, saya tidak ingin ada mockdi dalamnya. Rasanya aneh bagiku.
Mike de Klerk
21

Sekarang Anda bisa melakukannya (tidak yakin apakah itu jawaban yang sama dengan evilpie):

MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42
andreabedini
sumber
@ evilpie menjawab set atribut langsung di MyObject (kelas), bukan instansinya seperti milik Anda.
jfs
18

Coba kode di bawah ini:

$ python
>>> class Container(object):
...     pass 
...
>>> x = Container()
>>> x.a = 10
>>> x.b = 20
>>> x.banana = 100
>>> x.a, x.b, x.banana
(10, 20, 100)
>>> dir(x)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__',     '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'banana']
neldor
sumber
1
Bisakah Anda menjelaskan lebih banyak tentang apa ini? sementara kode mungkin berguna untuk menyelesaikan masalah ini, setelah dijelaskan bisa jauh lebih dari sekadar satu masalah.
DeadChex
1
@DeadChex Jelas itu menciptakan Kelas baru (objek) yang merupakan kelas kosong dengan properti objek, dan menyimpan atribut di dalam kelas. Ini bahkan lebih baik daripada menginstal lebih banyak modul, atau mengandalkan lambdas.
m3nda
2
Tidak yakin mengapa ini tidak memiliki lebih banyak upvotes. Apakah ada alasan untuk tidak menggunakan ini untuk kelas kontainer dasar? Tampaknya berfungsi dengan baik di Python 2.7, 2.6, dan 3.4
user5359531
17

Anda juga dapat menggunakan objek kelas secara langsung; itu menciptakan namespace:

class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')
Ernesto
sumber
9

seperti kata docs :

Catatan : objecttidak tidak memiliki __dict__, sehingga Anda tidak bisa atribut sewenang-wenang menetapkan untuk sebuah instance dari objectkelas.

Anda bisa menggunakan instance kelas dummy.

SilentGhost
sumber
2

Solusi ini sangat membantu selama pengujian. Membangun jawaban orang lain, saya melakukan ini di Python 2.7.9 (tanpa metode statis saya mendapatkan TypeError (metode tidak terikat ...):

In [11]: auth = type('', (), {})
In [12]: auth.func = staticmethod(lambda i: i * 2)
In [13]: auth.func(2)
Out[13]: 4
Robpol86
sumber
1

Objek mana yang Anda gunakan? Baru saja mencobanya dengan kelas sampel dan berhasil:

class MyClass:
  i = 123456
  def f(self):
    return "hello world"

b = MyClass()
b.c = MyClass()
setattr(b.c, 'test', 123)
b.c.test

Dan saya mendapat 123jawabannya.

Satu-satunya situasi di mana saya melihat kegagalan ini adalah jika Anda mencoba setattrobjek bawaan.

Pembaruan: Dari komentar ini adalah pengulangan: Mengapa Anda tidak dapat menambahkan atribut ke objek dengan python?

jneves
sumber
bc diatur ke objek () bukan kelas yang ditentukan
John
0

Datang ke ini pada sore hari tetapi di sini adalah pennyworth saya dengan objek yang kebetulan memegang beberapa jalur yang berguna dalam aplikasi tetapi Anda dapat mengadaptasinya untuk apa pun di mana Anda ingin sedikit informasi yang dapat Anda akses dengan getattr dan notasi titik (yang menurut saya pertanyaan ini sebenarnya tentang):

import os

def x_path(path_name):
    return getattr(x_path, path_name)

x_path.root = '/home/x'
for name in ['repository', 'caches', 'projects']:
    setattr(x_path, name, os.path.join(x_path.root, name))

Ini keren karena sekarang:

In [1]: x_path.projects
Out[1]: '/home/x/projects'

In [2]: x_path('caches')
Out[2]: '/home/x/caches'

Jadi ini menggunakan objek fungsi seperti jawaban di atas tetapi menggunakan fungsi untuk mendapatkan nilai (Anda masih bisa menggunakan (getattr, x_path, 'repository')daripada x_path('repository')jika Anda suka).

Paul Whipp
sumber
0

Jika kita dapat menentukan dan menggabungkan semua atribut dan nilai sebelum membuat objek bersarang, maka kita bisa membuat kelas baru yang menggunakan argumen kamus tentang pembuatan.

# python 2.7

class NestedObject():
    def __init__(self, initial_attrs):
        for key in initial_attrs:
            setattr(self, key, initial_attrs[key])

obj = someobject
attributes = { 'attr1': 'val1', 'attr2': 'val2', 'attr3': 'val3' }
obj.a = NestedObject(attributes)
>>> obj.a.attr1
'val1'
>>> obj.a.attr2
'val2'
>>> obj.a.attr3
'val3'

Kami juga dapat mengizinkan argumen kata kunci. Lihat posting ini .

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


obj.a = NestedObject(attr1='val1', attr2='val2', attr3= 'val3')
HarlemSquirrel
sumber
-2
di = {}
for x in range(20):
    name = '_id%s' % x
    di[name] = type(name, (object), {})
    setattr(di[name], "attr", "value")
lmokto
sumber
-2

Cara lain yang saya lihat, seperti ini:

import maya.cmds

def getData(objets=None, attrs=None):
    di = {}
    for obj in objets:
        name = str(obj)
        di[name]=[]
        for at in attrs:
            di[name].append(cmds.getAttr(name+'.'+at)[0])
    return di

acns=cmds.ls('L_vest_*_',type='aimConstraint')
attrs=['offset','aimVector','upVector','worldUpVector']

getData(acns,attrs)
Pablo Emmanuel De Leo
sumber
Anda dapat menambahkan dalam menambahkan nama attr this di [name] .append ([at, cmds.getAttr (name + '.' + at) [0]])
Pablo Emmanuel De Leo
1
Ini menambahkan ketergantungan non-standar yang sangat besar sementara yang sederhana class a: passmemberikan semua daya yang dibutuhkan.
Alexis Paques