Mendapatkan atribut kelas

106

Saya ingin mendapatkan atribut kelas, katakanlah:

class MyClass():
  a = "12"
  b = "34"

  def myfunc(self):
    return self.a

menggunakan MyClass.__dict__memberi saya daftar atribut dan fungsi, dan bahkan fungsi seperti __module__dan __doc__. While MyClass().__dict__memberi saya dict kosong kecuali saya secara eksplisit menetapkan nilai atribut dari instance itu.

Saya hanya ingin atributnya, dalam contoh di atas itu adalah: adanb

Mohamed Khamis
sumber
kemungkinan duplikat dari Atribut kelas inspeksi python
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Jawaban:

123

Coba modul inspeksi . getmembersdan berbagai tes akan membantu.

EDIT:

Sebagai contoh,

class MyClass(object):
    a = '12'
    b = '34'
    def myfunc(self):
        return self.a

>>> import inspect
>>> inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a)))
[('__class__', type),
 ('__dict__',
  <dictproxy {'__dict__': <attribute '__dict__' of 'MyClass' objects>,
   '__doc__': None,
   '__module__': '__main__',
   '__weakref__': <attribute '__weakref__' of 'MyClass' objects>,
   'a': '34',
   'b': '12',
   'myfunc': <function __main__.myfunc>}>),
 ('__doc__', None),
 ('__module__', '__main__'),
 ('__weakref__', <attribute '__weakref__' of 'MyClass' objects>),
 ('a', '34'),
 ('b', '12')]

Sekarang, metode dan atribut khusus membuat saya jengkel - itu dapat ditangani dengan beberapa cara, yang paling mudah adalah hanya dengan memfilter berdasarkan nama.

>>> attributes = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a)))
>>> [a for a in attributes if not(a[0].startswith('__') and a[0].endswith('__'))]
[('a', '34'), ('b', '12')]

... dan yang lebih rumit dapat menyertakan pemeriksaan nama atribut khusus atau bahkan metaclass;)

Matt Luongo
sumber
yup ini bagus! Saya menggunakan ini: attributes = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))) print [a[0] for a in attributes if '_' not in a[0]]
Mohamed Khamis
2
Hati-hati- itu tidak termasuk atribut like_this! Ini juga akan menghindari atribut "pribadi", yang mungkin Anda lakukan dengan sengaja.
Matt Luongo
Hai, saya menyukainya juga dengan sedikit klarifikasi: dalam ekspresi inspect.getmembers(MyClass, ..., MyClassdapat diganti dengan kelas atau objek, dan jika Anda memerlukan daftar nilai objek Anda, Anda harus mengganti MyClassdengan variabel objek Anda (atau selfjika Anda meletakkan ekspresi ini dalam def __repr__()metode seperti saya).
herve-guerin
Saya menggunakan ini (dalam Python3) untuk mendapatkan fungsi yang mencari nilai ' dict ': i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]dan kemudian ini hanya masalah mendapatkan kunci dari z.
double0darbo
43
def props(cls):   
  return [i for i in cls.__dict__.keys() if i[:1] != '_']

properties = props(MyClass)
Doug
sumber
7
Ini akan mencakup nama metode
lenhhoxung
10
Woudn't lebih jelas untuk memeriksa: if not i.startswith('_')bukan if i[:1] != '_'?
Mikaelblomkvistsson
2
Catatan: jika kita berbicara tentang kelas anak (warisan), maka .__dict__.keys()tidak akan menyertakan atribut dari orang tua.
vishes_shell
21

myfunc adalah atribut dari MyClass. Begitulah cara ditemukan saat Anda menjalankan:

myinstance = MyClass()
myinstance.myfunc()

Itu mencari atribut pada myinstancebernama myfunc, tidak menemukannya, melihat itu myinstanceadalah turunan dari MyClassdan mencarinya di sana.

Jadi daftar lengkap atributnya MyClassadalah:

>>> dir(MyClass)
['__doc__', '__module__', 'a', 'b', 'myfunc']

(Perhatikan bahwa saya menggunakan dir hanya sebagai cara cepat dan mudah untuk membuat daftar anggota kelas: itu hanya boleh digunakan dalam cara eksplorasi, bukan dalam kode produksi)

Jika Anda hanya ingin atribut tertentu, Anda akan perlu untuk menyaring daftar ini menggunakan beberapa kriteria, karena __doc__, __module__dan myfunctidak khusus dengan cara apapun, mereka atribut berada di persis dengan cara yang sama yang adanb yang.

Saya tidak pernah menggunakan modul inspeksi yang dirujuk oleh Matt dan Borealid, tetapi dari tautan singkat sepertinya ada tes untuk membantu Anda melakukan ini, tetapi Anda harus menulis fungsi predikat Anda sendiri, karena sepertinya itu yang Anda inginkan kira-kira adalah atribut yang tidak lulus isroutineujian dan tidak dimulai dan diakhiri dengan dua garis bawah.

Juga perhatikan: dengan menggunakan class MyClass():Python 2.7 Anda menggunakan kelas gaya lama yang sangat ketinggalan zaman. Kecuali Anda melakukannya dengan sengaja untuk kompatibilitas dengan pustaka yang sangat lama, Anda harus mendefinisikan kelas Anda sebagai class MyClass(object):. Di Python 3 tidak ada kelas "gaya lama", dan perilaku ini adalah default. Namun, menggunakan kelas gaya baru akan memberi Anda lebih banyak atribut yang ditentukan secara otomatis:

>>> class MyClass(object):
        a = "12"
        b = "34"
        def myfunc(self):
            return self.a
>>> dir(MyClass)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'myfunc']
Ben
sumber
6
Seseorang tidak dapat bergantung pada dir(): " Karena dir () disediakan terutama sebagai kemudahan untuk digunakan pada prompt interaktif, ia mencoba untuk menyediakan satu set nama yang menarik lebih dari itu mencoba untuk menyediakan satu set nama yang didefinisikan secara ketat atau konsisten , dan detailnya perilaku dapat berubah di seluruh rilis . "(lihat dokumentasidir() ).
Tadeck
@Tadeck: Poin bagus. Saya menggunakannya secara ilustratif daripada menyarankannya sebagai solusi, karena itu tidak akan dengan mudah memungkinkan Anda untuk memfilter atribut berdasarkan apa yang mereka rujuk. Tetapi saya harus lebih eksplisit tentang itu.
Ben
15

Mendapatkan hanya atribut instance itu mudah.
Tetapi mendapatkan juga atribut kelas tanpa fungsi sedikit lebih rumit.

Atribut instance saja

Jika Anda hanya perlu membuat daftar atribut contoh, gunakan saja
for attribute, value in my_instance. __dict__.items()

>>> from __future__ import (absolute_import, division, print_function)
>>> class MyClass(object):
...   def __init__(self):
...     self.a = 2
...     self.b = 3
...   def print_instance_attributes(self):
...     for attribute, value in self.__dict__.items():
...       print(attribute, '=', value)
...
>>> my_instance = MyClass()
>>> my_instance.print_instance_attributes()
a = 2
b = 3
>>> for attribute, value in my_instance.__dict__.items():
...   print(attribute, '=', value)
...
a = 2
b = 3

Instance dan atribut kelas

Untuk mendapatkan juga atribut kelas tanpa fungsi, triknya adalah dengan menggunakancallable() .

Tapi metode statis yang tidak selalucallable !

Oleh karena itu, daripada menggunakan callable(value)use
callable( getattr(MyClass, attribute))

Contoh

from __future__ import (absolute_import, division, print_function)

class MyClass(object):
   a = "12"
   b = "34"               # class attributes

   def __init__(self, c, d):
     self.c = c
     self.d = d           # instance attributes

   @staticmethod
   def mystatic():        # static method
       return MyClass.b

   def myfunc(self):      # non-static method
     return self.a

   def print_instance_attributes(self):
     print('[instance attributes]')
     for attribute, value in self.__dict__.items():
        print(attribute, '=', value)

   def print_class_attributes(self):
     print('[class attributes]')
     for attribute in self.__dict__.keys():
       if attribute[:2] != '__':
         value = getattr(self, attribute)
         if not callable(value):
           print(attribute, '=', value)

v = MyClass(4,2)
v.print_class_attributes()
v.print_instance_attributes()

Catatan: print_class_attributes() harus       tetapi tidak dalam hal bodoh dan sederhana ini@staticmethod
contoh .

Hasil untuk

$ python2 ./print_attributes.py
[class attributes]
a = 12
b = 34
[instance attributes]
c = 4
d = 2

Hasil yang sama untuk

$ python3 ./print_attributes.py
[class attributes]
b = 34
a = 12
[instance attributes]
c = 4
d = 2
olibre.dll
sumber
8

MyClass().__class__.__dict__

Namun, "hak" untuk melakukan ini adalah melalui modul inspeksi .

Borealid
sumber
6
MyClass().__class__.__dict__==MyClass.__dict__
yak
5
Komentar @ yak tidak sepenuhnya benar. Lihat perbedaan antara atribut class dan instance berikut ini. Lihat stackoverflow.com/questions/35805/… .
sholsapp
@ yak sebenarnya @ yuk. Tautan yang Anda berikan mengatakan itu MyClass().__class__.__dict__ != MyClass().__dict__, tetapi yak tidak termasuk tanda kurung di sisi kanan, dalam hal mana dia benar
shadi
2
import re

class MyClass:
    a = "12"
    b = "34"

    def myfunc(self):
        return self.a

attributes = [a for a, v in MyClass.__dict__.items()
              if not re.match('<function.*?>', str(v))
              and not (a.startswith('__') and a.endswith('__'))]

Untuk instance MyClass, seperti

mc = MyClass()

gunakan type(mc)sebagai pengganti MyClassdalam pemahaman daftar. Namun, jika seseorang secara dinamis menambahkan atribut ke mc, seperti mc.c = "42", atribut tersebut tidak akan muncul saat digunakan type(mc)dalam strategi ini. Ini hanya memberikan atribut kelas aslinya.

Untuk mendapatkan kamus lengkap untuk instance kelas, Anda perlu MENGGABUNGKAN kamus dari type(mc).__dict__dan mc.__dict__.

mc = MyClass()
mc.c = "42"

# Python 3.5
combined_dict = {**type(mc).__dict__, **mc.__dict__}

# Or Python < 3.5
def dict_union(d1, d2):
    z = d1.copy()
    z.update(d2)
    return z

combined_dict = dict_union(type(mc).__dict__, mc.__dict__)

attributes = [a for a, v in combined_dict.items()
              if not re.match('<function.*?>', str(v))
              and not (a.startswith('__') and a.endswith('__'))]
JD Graham
sumber
Solusi yang sangat rapi.
Gitnik
2

Saya tidak tahu apakah sesuatu yang serupa telah dibuat sekarang atau belum, tetapi saya membuat fungsi pencarian atribut yang bagus menggunakan vars (). vars () membuat kamus atribut kelas yang Anda lewati.

class Player():
    def __init__(self):
        self.name = 'Bob'
        self.age = 36
        self.gender = 'Male'

s = vars(Player())
#From this point if you want to print all the attributes, just do print(s)

#If the class has a lot of attributes and you want to be able to pick 1 to see
#run this function
def play():
    ask = input("What Attribute?>: ")
    for key, value in s.items():
        if key == ask:
            print("self.{} = {}".format(key, value))
            break
    else:
        print("Couldn't find an attribute for self.{}".format(ask))

Saya mengembangkan Petualangan Teks yang cukup besar dengan Python, kelas Pemain saya sejauh ini memiliki lebih dari 100 atribut. Saya menggunakan ini untuk mencari atribut tertentu yang perlu saya lihat.

Corey Bailey
sumber
sayangnya vars () tidak akan mengembalikan atribut kelas
user2682863
Sudahkah Anda mencoba menjalankan kode yang saya posting? Vars pasti dapat mengembalikan atribut kelas. Tunjukkan contoh bagaimana tidak? Mungkin kode saya salah. Tapi menugaskan vars () ke variabel dan menggunakan kunci, pencarian nilai melalui variabel itu dapat mengembalikan atribut kelas.
Corey Bailey
kelas T: x = 1; t = T (); vars (t)
user2682863
Anda harus menunggu sampai saya selesai bekerja untuk menunjukkannya dengan benar. Tapi kode Anda salah. Objek kelas Anda perlu mendefinisikan __init __ (self) dan x harus self.x = 1. Kemudian tetapkan t = T () dan gunakan print (vars (t)) dan itu akan menunjukkan kepada Anda kamus dari semua atribut kelas.
Corey Bailey
tidak, itu adalah atribut instance bukan atribut kelas, banyak sub-kelas tidak pernah memanggil init . Seperti yang saya katakan, vars () tidak akan mengembalikan atribut kelas, Anda perlu menggunakan dir () atau inspect.getmembers ()
user2682863
2

Ini bisa dilakukan tanpa pemeriksaan, kurasa.

Ikuti kelas berikut:

 class Test:
   a = 1
   b = 2

   def __init__(self):
     self.c = 42

   @staticmethod
   def toto():
     return "toto"

   def test(self):
     return "test"

Melihat anggota beserta tipenya:

t = Test()
l = [ (x, eval('type(x.%s).__name__' % x)) for x in dir(a) ]

... memberikan:

[('__doc__', 'NoneType'),
 ('__init__', 'instancemethod'),
 ('__module__', 'str'),
 ('a', 'int'),
 ('b', 'int'),
 ('c', 'int'),
 ('test', 'instancemethod'),
 ('toto', 'function')]

Jadi untuk menghasilkan hanya variabel, Anda hanya perlu memfilter hasil berdasarkan jenis, dan nama tidak dimulai dengan '__'. Misalnya

filter(lambda x: x[1] not in ['instancemethod', 'function'] and not x[0].startswith('__'), l)

[('a', 'int'), ('b', 'int'), ('c', 'int')] # actual result

Itu dia.

Catatan: jika Anda menggunakan Python 3, konversikan iterator menjadi daftar.

Jika Anda menginginkan cara yang lebih efektif untuk melakukannya, gunakan inspect .

carmellose
sumber
2

Python 2 & 3, menghapus impor, memfilter objek berdasarkan alamatnya

Solusi singkatnya:

Kembalikan dikt {attribute_name: atribut_value} , objek difilter. yaitu{'a': 1, 'b': (2, 2), 'c': [3, 3]}

{k: val for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)}

Kembali daftar [nama_tribut] , objek difilter. yaitu['a', 'b', 'c', 'd']

[k for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)]

Kembalikan daftar [nilai_atribut] , objek yang difilter. yaitu[1, (2, 2), [3, 3], {4: 4}]

[val for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)]

Tidak memfilter objek

Menghapus ifkondisi tersebut. Kembali{'a': 1, 'c': [3, 3], 'b': (2, 2), 'e': <function <lambda> at 0x7fc8a870fd70>, 'd': {4: 4}, 'f': <object object at 0x7fc8abe130e0>}

{k: val for k, val in self.__dict__.items()}

Solusi lama

Selama implementasi default __repr__tidak ditimpa , ifpernyataan akan dikembalikan Truejika representasi heksadesimal dari lokasi dalam memori valada dalam __repr__string kembali.

Mengenai implementasi default __repr__Anda dapat menemukan berguna jawaban ini . Pendeknya:

def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

Yang mengembalikan string seperti:

<__main__.Bar object at 0x7f3373be5998>

Lokasi dalam memori setiap elemen didapat melalui id()metode.

Python Docs mengatakan tentang id ():

Kembalikan "identitas" dari suatu objek. Ini adalah bilangan bulat yang dijamin unik dan konstan untuk objek ini selama masa pakainya. Dua objek dengan masa pakai yang tidak tumpang tindih mungkin memiliki nilai id () yang sama.

Detail implementasi CPython: Ini adalah alamat objek di memori.


Coba sendiri

class Bar:

    def __init__(self):

        self.a = 1
        self.b = (2, 2)
        self.c = [3, 3]
        self.d = {4: 4}
        self.e = lambda: "5"
        self.f = object()

    #__str__ or __repr__ as you prefer
    def __str__(self):
        return "{}".format(

            # Solution in Short Number 1
            {k: val for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)}

        )

# Main
print(Bar())

Keluaran:

{'a': 1, 'c': [3, 3], 'b': (2, 2), 'd': {4: 4}}

Catatan :

  • Diuji dengan Python 2.7.13dan Python3.5.3

  • Dalam Python 2.x .iteritems()lebih disukai daripada.items()

Marco DG
sumber
1

Saya baru-baru ini perlu mencari tahu sesuatu yang mirip dengan pertanyaan ini, jadi saya ingin memposting beberapa info latar belakang yang mungkin berguna bagi orang lain yang menghadapi hal yang sama di masa mendatang.

Berikut cara kerjanya dengan Python (dari https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy ):

MyClassadalah objek kelas, MyClass()adalah turunan dari objek kelas. Sebuah instance __dict__hanya memiliki atribut dan metode khusus untuk instance itu (misalnya self.somethings). Jika atribut atau metode adalah bagian dari kelas, itu ada di kelas __dict__. Ketika Anda melakukannya MyClass().__dict__, sebuah instance MyClassdibuat tanpa atribut atau metode selain atribut kelas, sehingga kosong__dict__

Jadi jika Anda berkata print(MyClass().b), Python pertama-tama memeriksa dikt instance baru MyClass().__dict__['b']dan gagal menemukannya b. Ia kemudian memeriksa kelas MyClass.__dict__['b']dan menemukan b.

Itulah mengapa Anda membutuhkan inspectmodul, untuk meniru proses pencarian yang sama.

Scott Howard
sumber
2
Scott - Komentar yang diposting sebagai jawaban harus dihapus, jika tidak, kami akan tenggelam di dalamnya. Namun, jawaban parsial atau "dorongan membantu" menuju solusi masih merupakan jawaban . Anda akan melihat bagaimana saya mengubah urutan posting Anda; semoga maksudmu tetap kuat. Jika tidak, Anda dapat mengeditnya lebih lanjut menjadi bentuk. Bersulang!
Mogsdad
1

Anda dapat menggunakan dir()pemahaman daftar untuk mendapatkan nama atribut:

names = [p for p in dir(myobj) if not p.startswith('_')]

Gunakan getattr()untuk mendapatkan atributnya sendiri:

attrs = [getattr(myobj, p) for p in dir(myobj) if not p.startswith('_')]
Rotareti
sumber
1

Solusi saya untuk mendapatkan semua atribut (bukan metode) dari kelas (jika kelas memiliki docstring yang ditulis dengan benar yang memiliki atribut yang dieja dengan jelas):

def get_class_attrs(cls):
    return re.findall(r'\w+(?=[,\)])', cls.__dict__['__doc__'])

Bagian ini cls.__dict__['__doc__']mengekstrak docstring dari kelas.

Henry On
sumber
1

Mengapa Anda perlu membuat daftar atribut? Tampaknya kelas Anda secara semantik adalah sebuah koleksi. Dalam kasus ini saya merekomendasikan untuk menggunakan enum:

import enum

class myClass(enum.Enum):
     a = "12"
     b = "34"

Sebutkan atribut Anda? Tidak ada yang lebih mudah dari ini:

for attr in myClass:
    print("Name / Value:", attr.name, attr.value)
VengaVenga
sumber
1

Jika Anda ingin "mendapatkan" atribut, ada jawaban yang sangat sederhana , yang seharusnya sudah jelas: getattr

class MyClass(object):
a = '12'
b = '34'
def myfunc(self):
    return self.a

>>> getattr(MyClass, 'a')
'12'

>>> getattr(MyClass, 'myfunc')
<function MyClass.myfunc at 0x10de45378>

Ia bekerja bagus baik di Python 2.7 dan Python 3.x.

Jika Anda menginginkan daftar item ini, Anda masih perlu menggunakan inspect.

fralau
sumber
1
Apakah jawaban itu terlalu sederhana dan terlalu tepat untuk mendapatkan poin, dan bahkan pantas mendapatkan poin buruk? Tampaknya ekonomi dan kesederhanaan tidak lagi membuahkan hasil saat ini.
fralau
0

dua fungsi:

def get_class_attr(Cls) -> []:
    import re
    return [a for a, v in Cls.__dict__.items()
              if not re.match('<function.*?>', str(v))
              and not (a.startswith('__') and a.endswith('__'))]

def get_class_attr_val(cls):
    attr = get_class_attr(type(cls))
    attr_dict = {}
    for a in attr:
        attr_dict[a] = getattr(cls, a)
    return attr_dict

menggunakan:

>>> class MyClass:
    a = "12"
    b = "34"
    def myfunc(self):
        return self.a

>>> m = MyClass()
>>> get_class_attr_val(m)
{'a': '12', 'b': '34'}
syal merah
sumber
0

Berikut ini yang saya inginkan.

Uji Data

class Base:
    b = 'b'


class MyClass(Base):
    a = '12'

    def __init__(self, name):
        self.name = name

    @classmethod
    def c(cls):
        ...

    @property
    def p(self):
        return self.a

    def my_fun(self):
        return self.name
print([name for name, val in inspect.getmembers(MyClass) if not name.startswith('_') and not callable(val)])  # need `import inspect`
print([_ for _ in dir(MyClass) if not _.startswith('_') and not callable(getattr(MyClass, _))])
# both are equ: ['a', 'b', 'p']

my_instance = MyClass('c')
print([_ for _ in dir(my_instance) if not _.startswith('_') and not callable(getattr(my_instance, _))])
# ['a', 'b', 'name', 'p']
Carson
sumber
-2

Saya tahu ini terjadi tiga tahun lalu, tetapi bagi mereka yang memiliki pertanyaan ini di masa depan, bagi saya:

class_name.attribute 

bekerja dengan baik.

Jim Jam
sumber
3
kecuali jika Anda mendapatkan AttributeError.
rady
Anda tidak selalu tahu apa attributeitu sebelumnya.
Matt Luongo
-3

Anda bisa menggunakan MyClass.__attrs__. Itu hanya memberikan semua atribut kelas itu. Tidak ada lagi.

jimxliu
sumber
AttributeError: objek tipe 'X' tidak memiliki atribut ' attrs '
Ramazan Polat