Iterasi atribut objek dalam python


162

Saya memiliki objek python dengan beberapa atribut dan metode. Saya ingin beralih pada atribut objek.

class my_python_obj(object):
    attr1='a'
    attr2='b'
    attr3='c'

    def method1(self, etc, etc):
        #Statements

Saya ingin membuat kamus yang berisi semua atribut objek dan nilai-nilai mereka saat ini, tetapi saya ingin melakukannya dengan cara yang dinamis (jadi jika nanti saya menambahkan atribut lain saya tidak harus ingat untuk memperbarui fungsi saya juga).

Dalam variabel php dapat digunakan sebagai kunci, tetapi objek dalam python tidak dapat ditulis dan jika saya menggunakan notasi titik untuk ini, ia menciptakan atribut baru dengan nama var saya, yang bukan maksud saya.

Hanya untuk memperjelas:

def to_dict(self):
    '''this is what I already have'''
    d={}
    d["attr1"]= self.attr1
    d["attr2"]= self.attr2
    d["attr3"]= self.attr3
    return d

·

def to_dict(self):
    '''this is what I want to do'''
    d={}
    for v in my_python_obj.attributes:
        d[v] = self.v
    return d

Pembaruan: Dengan atribut yang saya maksud hanya variabel objek ini, bukan metode.



@ Sean Secara khusus, stackoverflow.com/a/1251789/4203 adalah cara untuk pergi; Anda akan menggunakan predicateargumen opsional untuk meneruskan fungsi callable yang akan memfilter (terikat?) fungsi (yang menghilangkan metode).
Hank Gay

Per sean, apakah ini menjawab pertanyaan Anda? Bagaimana cara menghitung properti objek dengan Python?
Davis Herring

Jawaban:


227

Dengan asumsi Anda memiliki kelas seperti

>>> class Cls(object):
...     foo = 1
...     bar = 'hello'
...     def func(self):
...         return 'call me'
...
>>> obj = Cls()

memanggil dirobjek memberi Anda kembali semua atribut objek itu, termasuk atribut khusus python. Meskipun beberapa atribut objek dapat dipanggil, seperti metode.

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo', 'func']

Anda selalu dapat memfilter metode khusus dengan menggunakan pemahaman daftar.

>>> [a for a in dir(obj) if not a.startswith('__')]
['bar', 'foo', 'func']

atau jika Anda lebih suka peta / filter.

>>> filter(lambda a: not a.startswith('__'), dir(obj))
['bar', 'foo', 'func']

Jika Anda ingin menyaring metode, Anda dapat menggunakan builtin callablesebagai tanda centang.

>>> [a for a in dir(obj) if not a.startswith('__') and not callable(getattr(obj, a))]
['bar', 'foo']

Anda juga bisa memeriksa perbedaan antara kelas Anda dan objek instansinya menggunakan.

>>> set(dir(Cls)) - set(dir(object))
set(['__module__', 'bar', 'func', '__dict__', 'foo', '__weakref__'])

1
Ini adalah ide yang buruk, saya bahkan tidak akan menyarankannya, tetapi jika Anda mau, setidaknya berikan peringatannya.
Julian

2
@ Meitham @Pablo Apa yang salah terutama adalah bahwa Anda tidak perlu melakukan ini. Atribut mana yang Anda minati harus inklusif bukan eksklusif . Seperti yang sudah Anda lihat, Anda memasukkan metode, dan segala upaya untuk mengecualikannya akan salah, karena mereka akan melibatkan hal-hal buruk seperti callableatau types.MethodType. Apa yang terjadi jika saya menambahkan atribut yang disebut "_foo" yang menyimpan beberapa hasil sementara, atau struktur data internal? Apa yang terjadi jika saya hanya menambahkan atribut ke kelas, tanpa name, katakan, dan sekarang tiba-tiba itu dimasukkan.
Julian

3
@Julian kode di atas akan memilih keduanya _foodan namekarena sekarang atribut dari objek. Jika Anda tidak ingin memasukkannya, Anda dapat mengecualikannya dalam kondisi pemahaman daftar. Kode di atas adalah idiom python umum dan cara populer mengintrospeksi objek python.
Meitham

30
Sebenarnya obj .__ dict__ adalah (mungkin) lebih baik untuk tujuan ini.
Dave

5
@Julian dir()hanya "kebohongan" ketika melewati metaclasses (kasus tepi ekstrem) atau kelas dengan atribut yang dihiasi oleh @DynamicClassAttribute(kasus tepi ekstrem lain). Dalam kedua kasus, memanggil dirs()bungkus inspect.getmembers()menyelesaikan ini. Untuk objek standar, bagaimanapun, pendekatan pemahaman daftar solusi ini menyaring non-callable benar - benar cukup. Bahwa beberapa Pythonistas akan menamakannya "ide buruk" membuatku bingung, tapi ... untuk masing-masing, kurasa?
Cecil Curry

57

secara umum letakkan __iter__metode di kelas Anda dan beralih melalui atribut objek atau letakkan kelas mixin ini di kelas Anda.

class IterMixin(object):
    def __iter__(self):
        for attr, value in self.__dict__.iteritems():
            yield attr, value

Kelas Anda:

>>> class YourClass(IterMixin): pass
...
>>> yc = YourClass()
>>> yc.one = range(15)
>>> yc.two = 'test'
>>> dict(yc)
{'one': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 'two': 'test'}

ini hanya berfungsi ketika onedan twodidefinisikan setelah yc = YourClass(). Pertanyaan itu bertanya tentang perulangan melalui attris yang ada di YourClass(). Juga, mewarisi IterMixinkelas mungkin tidak selalu tersedia sebagai solusi.
Xiao

4
vars(yc)memberikan hasil yang sama tanpa harus mewarisi dari kelas lain.
Nathan

1
Komentar saya di atas tidak sepenuhnya benar; vars(yc)hampir sama, tetapi kamus yang mengembalikan Anda adalah milik instance __dict__, jadi memodifikasi itu akan tercermin pada instance. Ini dapat menyebabkan masalah jika Anda tidak berhati-hati, sehingga metode di atas terkadang lebih baik (meskipun menyalin kamus mungkin lebih mudah). Juga, perhatikan bahwa meskipun kamus yang dikembalikan di atas bukan milik instance __dict__, nilainya sama, jadi memodifikasi nilai yang dapat diubah dapat tetap tercermin dalam atribut instance.
Nathan

25

Seperti yang disebutkan dalam beberapa jawaban / komentar sudah, objek Python sudah menyimpan kamus atribut mereka (metode tidak termasuk). Ini dapat diakses sebagai __dict__, tetapi cara yang lebih baik adalah dengan menggunakan vars(outputnya sama, meskipun). Perhatikan bahwa memodifikasi kamus ini akan mengubah atribut pada instance! Ini bisa bermanfaat, tetapi juga berarti Anda harus berhati-hati dengan cara Anda menggunakan kamus ini. Ini contoh singkatnya:

class A():
    def __init__(self, x=3, y=2, z=5):
        self.x = x
        self._y = y
        self.__z__ = z

    def f(self):
        pass

a = A()
print(vars(a))
# {'x': 3, '_y': 2, '__z__': 5}
# all of the attributes of `a` but no methods!

# note how the dictionary is always up-to-date
a.x = 10
print(vars(a))
# {'x': 10, '_y': 2, '__z__': 5}

# modifying the dictionary modifies the instance attribute
vars(a)["_y"] = 20
print(vars(a))
# {'x': 10, '_y': 20, '__z__': 5}

Menggunakan dir(a)adalah pendekatan yang aneh, jika tidak langsung buruk, untuk masalah ini. Baik jika Anda benar-benar perlu untuk mengulangi semua atribut dan metode kelas (termasuk metode khusus seperti __init__). Namun, ini sepertinya bukan yang Anda inginkan, dan bahkan jawaban yang diterima mengenai hal ini buruk dengan menerapkan beberapa penyaringan getas untuk mencoba menghapus metode dan hanya meninggalkan atribut; Anda dapat melihat bagaimana ini akan gagal untuk kelas yang Aditentukan di atas.

(menggunakan __dict__telah dilakukan dalam beberapa jawaban, tetapi mereka semua mendefinisikan metode yang tidak perlu daripada menggunakannya secara langsung. Hanya komentar yang menyarankan untuk menggunakan vars).


1
Sesuatu yang saya belum mengerti dengan pendekatan vars () adalah bagaimana menangani situasi di mana kelas A memiliki anggota yang merupakan objek lain yang tipenya adalah kelas B. yang didefinisikan pengguna-pengguna (a) tampaknya memanggil __repr __ () pada anggota tipe B. __repr __ (), seperti yang saya mengerti, seharusnya mengembalikan string. Tetapi ketika saya memanggil vars (a), sepertinya masuk akal untuk panggilan ini untuk mengembalikan dict bersarang, alih-alih dict dengan representasi string B.
plafratt

1
Jika Anda memiliki a.bbeberapa kelas khusus Bmaka vars(a)["b"] is a.b, seperti yang diharapkan; tidak ada hal lain yang benar-benar masuk akal (anggap a.bsebagai gula sintaksis a.__dict__["b"]). Jika Anda memiliki d = vars(a)dan memanggil repr(d)maka itu akan memanggil repr(d["b"])sebagai bagian dari mengembalikan itu sendiri repr-string karena hanya kelas yang Bbenar-benar tahu bagaimana itu harus direpresentasikan sebagai string.
Nathan

2

Objek dalam python menyimpan atribut mereka (termasuk fungsi) dalam sebuah dict yang disebut __dict__. Anda dapat (tetapi umumnya tidak boleh) menggunakan ini untuk mengakses atribut secara langsung. Jika Anda hanya ingin daftar, Anda juga bisa menelepon dir(obj), yang mengembalikan iterable dengan semua nama atribut, yang kemudian bisa Anda sampaikan kegetattr .

Namun, perlu melakukan apa pun dengan nama-nama variabel biasanya desain yang buruk. Mengapa tidak menyimpannya di koleksi?

class Foo(object):
    def __init__(self, **values):
        self.special_values = values

Anda kemudian dapat beralih di atas tombol dengan for key in obj.special_values:


Bisakah Anda jelaskan sedikit lagi bagaimana saya menggunakan koleksi untuk mencapai apa yang saya inginkan?
Pablo Mescher

1
Saya tidak menambahkan atribut ke objek secara dinamis. Variabel yang saya miliki akan tetap sama untuk waktu yang lama, namun saya pikir akan menyenangkan untuk dapat melakukan sesuatu seperti apa yang saya inginkan.
Pablo Mescher

1
class someclass:
        x=1
        y=2
        z=3
        def __init__(self):
           self.current_idx = 0
           self.items = ["x","y","z"]
        def next(self):
            if self.current_idx < len(self.items):
                self.current_idx += 1
                k = self.items[self.current_idx-1]
                return (k,getattr(self,k))
            else:
                raise StopIteration
        def __iter__(self):
           return self

maka sebut saja itu sebagai iterable

s=someclass()
for k,v in s:
    print k,"=",v

Ini sepertinya terlalu rumit. Jika saya tidak punya pilihan, saya lebih suka tetap dengan apa yang saya lakukan sekarang.
Pablo Mescher

0

Jawaban yang benar untuk ini adalah bahwa Anda seharusnya tidak melakukannya. Jika Anda menginginkan hal semacam ini, gunakan saja dict, atau Anda harus menambahkan atribut secara eksplisit ke suatu wadah. Anda dapat mengotomatiskannya dengan mempelajari tentang dekorator.

Secara khusus, metode1 pada contoh Anda sama baiknya dengan atribut.


2
Ya, saya tahu saya seharusnya tidak .. tetapi itu membantu saya memahami bagaimana segala sesuatunya bekerja. Saya berasal dari latar belakang php dan biasa melakukan hal-hal semacam ini setiap hari
Pablo Mescher

Anda meyakinkan saya. Terus memperbarui metode to_dict () jauh lebih mudah daripada jawaban apa pun sejauh ini.
Pablo Mescher

1
Apakah ada orang yang tersisa yang akan menahan diri dari nasihat dogmatis. Pemrograman dinamis akan membakar Anda jika Anda tidak hati-hati, tetapi fitur dalam bahasa yang akan digunakan.
Henrik Vendelbo

0

Untuk python 3.6

class SomeClass:

    def attr_list(self, should_print=False):

        items = self.__dict__.items()
        if should_print:
            [print(f"attribute: {k}    value: {v}") for k, v in items]

        return items

6
Bisakah Anda menambahkan beberapa penjelasan pada posting Anda, sehingga pythonis yang tidak profesional juga bisa mendapat manfaat dari jawaban Anda?
not2qubit

-3

Untuk semua fanatik pythonian di luar sana, saya yakin Johan Cleeze akan menyetujui dogmatisme Anda;). Saya meninggalkan jawaban ini terus-menerus meremehkannya. Itu sebenarnya membuat saya lebih percaya diri. Tinggalkan komentar kamu ayam!

Untuk python 3.6

class SomeClass:

    def attr_list1(self, should_print=False):

        for k in self.__dict__.keys():
            v = self.__dict__.__getitem__(k)
            if should_print:
                print(f"attr: {k}    value: {v}")

    def attr_list(self, should_print=False):

        b = [(k, v) for k, v in self.__dict__.items()]
        if should_print:
            [print(f"attr: {a[0]}    value: {a[1]}") for a in b]
        return b

Apakah ada alasan Anda memiliki dua jawaban untuk pertanyaan ini?
Nathan

@Nathan ada karena satu lebih verbose dan lainnya pythonian. Saya juga tidak tahu mana yang berjalan lebih cepat sehingga saya memutuskan untuk membiarkan pembaca memilih.
Bebek Karet

@ nathan apakah Anda python atau tumpukan meluap polisi? Ada berbagai gaya pengkodean. Saya lebih suka yang bukan pythonic karena saya bekerja dalam banyak bahasa dan teknologi dan saya pikir ini cenderung istimewa. Namun demikian, pemahaman Iist itu keren dan ringkas. Jadi mengapa tidak membeli pembaca yang tercerahkan keduanya?
Bebek Karet

Untuk semua fanatik pythonian di luar sana, saya yakin Johan Cleeze akan menyetujui dogmatisme Anda;). Saya meninggalkan jawaban ini terus-menerus meremehkannya. Itu sebenarnya membuat saya lebih percaya diri. Tinggalkan komentar kamu ayam!
Bebek Karet
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.