Bir sınıfın niteliklerini alma


110

Bir sınıfın niteliklerini almak istiyorum, diyelim ki:

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

  def myfunc(self):
    return self.a

kullanmak MyClass.__dict__bana özniteliklerin ve işlevlerin bir listesini verir ve hatta __module__ve gibi işlevler verir __doc__. Bu MyClass().__dict__örneğin öznitelik değerini açıkça ayarlamadığım sürece while bana boş bir dikte veriyor.

Sadece nitelikleri istiyorum, yukarıdaki örnekte bunlar şöyle olurdu: aveb


Yanıtlar:


126

İnceleme modülünü deneyin . getmembersve çeşitli testler yardımcı olmalıdır.

DÜZENLE:

Örneğin,

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

Şimdi, özel yöntemler ve nitelikler sinirimi bozuyor - bunlar birkaç şekilde halledilebilir, en kolayı isme göre filtrelemektir.

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

... ve daha karmaşık olanları özel nitelik adı kontrollerini ve hatta metasınıfları içerebilir;)


evet bu harika! Bunu kullandım: 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
Dikkatli olun - bu özellikler içermez like_this! Ayrıca, bilerek yapmış olabileceğiniz "özel" özelliklerden de kaçınır.
Matt Luongo

İfadesinde: Merhaba, hafif açıklama ile çok sevilen inspect.getmembers(MyClass, ..., MyClassbir sınıf veya bir nesne ile değiştirilebilir ve size nesnelerin değerler listesini gerekiyorsa, değiştirmeniz gerekir MyClassnesne değişkeni tarafından (ya selfsen koyarsanız bu ifade def __repr__()benim gibi bir yöntemde).
herve-guerin

Bunu (Python3'te) ' dict ' değerini arayan bir işlev elde etmek için kullandım : i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]ve sonra bu sadece anahtarları z'den almak meselesi.
double0darbo

42
def props(cls):   
  return [i for i in cls.__dict__.keys() if i[:1] != '_']

properties = props(MyClass)

7
Bu, yöntem adlarını içerecek
lenhhoxung

10
Kontrol etmek daha açık olamazdı: if not i.startswith('_')yerine if i[:1] != '_'?
Mikaelblomkvistsson

2
Not: Çocuk sınıfı (miras) hakkında konuşursak .__dict__.keys(), ebeveynden gelen nitelikleri dahil etmeyecektir.
vishes_shell

21

myfunc olduğu bir özelliği MyClass. Koştuğunuzda böyle bulunur:

myinstance = MyClass()
myinstance.myfunc()

Bu bir özellik için arar myinstanceadında myfuncbir tane bulmazsa, o görür myinstancebir örneği olup MyClassve orada arar.

Öyleyse için özelliklerin tam listesi MyClass:

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

(Dir'i, sınıfın üyelerini listelemenin hızlı ve kolay bir yolu olarak kullandığımı unutmayın: yalnızca keşif amaçlı kullanılmalıdır, üretim kodunda kullanılmamalıdır)

Yalnızca belirli özelliklerini istiyorsanız, çünkü bazı kriterleri kullanarak bu listeye filtre gerekir __doc__, __module__ve myfuncherhangi bir şekilde özel değildir onlar tam olarak aynı şekilde, konum özelliklerini ave bvardır.

Matt ve Borealid tarafından atıfta bulunulan inceleme modülünü hiç kullanmadım, ancak kısa bir bağlantıdan bunu yapmanıza yardımcı olacak testler var gibi görünüyor, ancak istediğiniz gibi göründüğü için kendi yüklem fonksiyonunuzu yazmanız gerekecek kabaca nitelikleri olan yok geçmesine isroutinetesti ve başlangıç ve iki alt çizgi ile bitmez.

Ayrıca unutmayın: class MyClass():Python 2.7'yi kullanarak çılgınca tarihi geçmiş eski stil sınıfları kullanıyorsunuz. Son derece eski kütüphanelerle uyumluluk için kasıtlı olarak yapmadığınız sürece, bunun yerine sınıfınızı olarak tanımlamalısınız class MyClass(object):. Python 3'te "eski tarz" sınıflar yoktur ve bu davranış varsayılandır. Ancak, yeni stil sınıflarını kullanmak size çok daha fazla otomatik olarak tanımlanmış nitelikler kazandıracaktır:

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

6
Bir bağlı olamaz dir(): " dir () kullanılmak için bir kolaylık olarak öncelikle tedarik edildiğinden interaktif istemi, bir o isimleri titizlikle ya da tutarlı tanımlanmış seti tedarik etmeye çalışır daha isimlerin ilginç bir set temin etmeye çalışır ve onun ayrıntılı davranış sürümler arasında değişebilir . "( belgelerinedir() bakın ).
Tadeck

@Tadeck: İyi nokta. Bunu bir çözüm olarak önermektense açıklayıcı bir şekilde kullanıyordum, çünkü öznitelikleri ifade ettikleri şeye göre filtrelemenize kolayca izin vermezdi. Ama bu konuda daha açık olmalıyım.
Ben

17

Yalnızca örnek özniteliklerini almak kolaydır.
Ancak işlevler olmadan sınıf özniteliklerini elde etmek de biraz daha zordur.

Yalnızca örnek öznitelikleri

Yalnızca örnek niteliklerini listelemeniz gerekiyorsa, sadece kullanın
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

Örnek ve sınıf öznitelikleri

İşlevler olmadan sınıf özniteliklerini de elde etmek için işin püf noktası kullanmaktır callable().

Ama statik yöntemler vardır her zaman değilcallable !

Bu nedenle, callable(value)use
callable( getattr(MyClass, attribute))

Misal

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

Not:       Bu aptalca ve basit örnekte print_class_attributes()olmalı ama olmamalı .@staticmethod

Sonuç

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

İçin aynı sonuç

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

8

MyClass().__class__.__dict__

Ancak "doğru", bunu inceleme modülü aracılığıyla yapmaktı .


6
MyClass().__class__.__dict__==MyClass.__dict__
yak

5
@ yak'ın yorumu pek doğru değil. Sınıf ve örnek nitelikleri arasındaki farklar hakkında aşağıdakilere bakın. Bkz stackoverflow.com/questions/35805/... .
sholsapp

@sholsapp aslında @yak doğru. Sağladığınız bağlantı diyor ki MyClass().__class__.__dict__ != MyClass().__dict__, ancak yak, doğru olduğu takdirde sağ taraftaki parantezleri
içermiyor

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('__'))]

MyClass'ın bir örneği için, örneğin

mc = MyClass()

liste anlamasında type(mc)yerine kullanın MyClass. Bununla birlikte, biri dinamik mcolarak gibi bir öznitelik eklerse, öznitelik bu stratejide mc.c = "42"kullanılırken type(mc)görünmez. Yalnızca orijinal sınıfın niteliklerini verir.

Bir sınıf örneği için tam bir sözlük almak için, sözlükleri KOMBİNE gerekir type(mc).__dict__ve 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('__'))]

Gerçekten temiz çözüm.
Gitnik

2

Şimdiye kadar benzer bir şey yapılıp yapılmadığını bilmiyorum ama vars () kullanarak güzel bir öznitelik arama fonksiyonu yaptım. vars (), içinden geçtiğiniz bir sınıfın özniteliklerinin bir sözlüğünü oluşturur.

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

Python'da oldukça büyük bir Metin Macerası geliştiriyorum, Oyuncu sınıfımın şimdiye kadar 100'den fazla özelliği var. Bunu görmem gereken belirli öznitelikleri aramak için kullanıyorum.


ne yazık ki vars () sınıf özniteliklerini döndürmeyecek
user2682863

Gönderdiğim kodu çalıştırmayı denediniz mi? Vars, kesinlikle sınıf özniteliklerini döndürebilir. Nasıl olmadığına dair bir örnek göster bana? Belki kodum yanlıştır. Ancak bir değişkene değişken () atamak ve bir anahtar kullanmak, bu değişken aracılığıyla değer araması sınıf özniteliklerini döndürebilir.
Corey Bailey

T sınıfı: x = 1; t = T (); vars (t)
user2682863

Sana düzgün bir şekilde göstermek için işten çıkana kadar beklemen gerekecek. Ancak kodunuz yanlış. Sınıf nesnenizin __init __ (self) tanımlaması ve x'in self.x = 1 olması gerekir. Daha sonra t = T () atayın ve print (vars (t)) kullanın, bu size tüm sınıf özniteliklerinin bir sözlüğünü gösterecektir.
Corey Bailey

hayır, bunlar sınıf öznitelikleri değil örnek öznitelikleridir, birçok alt sınıf asla init'i çağırmaz . Dediğim gibi, vars () sınıf özniteliklerini döndürmez, dir () veya inspect.getmembers () kullanmanız gerekir
user2682863

2

Bu teftiş edilmeden yapılabilir sanırım.

Aşağıdaki dersi alın:

 class Test:
   a = 1
   b = 2

   def __init__(self):
     self.c = 42

   @staticmethod
   def toto():
     return "toto"

   def test(self):
     return "test"

Üyelere türleriyle birlikte bakmak:

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

... verir:

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

Bu nedenle, yalnızca değişkenlerin çıktısını almak için sonuçları türe ve '__' ile başlamayan adlara göre filtrelemeniz yeterlidir. Örneğin

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

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

Bu kadar.

Not: Python 3 kullanıyorsanız yineleyicileri listelere dönüştürün.

Bunu yapmanın daha sağlam bir yolunu istiyorsanız, inspect kullanın .


2

Python 2 ve 3, içe aktarmadan, nesneleri adreslerine göre filtreler

Kısaca çözümler:

Döndür dikte {öznitelik_adı: öznitelik_değer} , nesneler filtrelenir. yani{'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)}

Döndürme listesi [öznitelik_simleri] , filtrelenen nesneler. yani['a', 'b', 'c', 'd']

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

Döndürme listesi [öznitelik_değerleri] , filtrelenen nesneler. yani[1, (2, 2), [3, 3], {4: 4}]

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

Nesneleri filtrelememek

ifKoşul kaldırılıyor . Dönüş{'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()}

Uzun çözüm

Sürece varsayılan uygulama olarak __repr__kılınır değilif deyimi dönecektir Trueanısına yerin onaltılık gösterimi eğer valiçindedir __repr__dönüş dize.

Varsayılan uygulamanızla ilgili olarak, bu cevap__repr__ yararlı olabilir . Kısacası:

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

Wich şöyle bir dize döndürür:

<__main__.Bar object at 0x7f3373be5998>

Her bir elemanın hafızasındaki konumu id()yöntemle elde edilir.

Python Docs id () hakkında şunları söylüyor:

Bir nesnenin "kimliğini" döndürür. Bu, ömrü boyunca bu nesne için benzersiz ve sabit olması garanti edilen bir tamsayıdır. Örtüşmeyen yaşam sürelerine sahip iki nesne aynı id () değerine sahip olabilir.

CPython uygulama ayrıntısı: Bu, bellekteki nesnenin adresidir.


Kendiniz deneyin

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

Çıktı:

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

Not :

  • Python 2.7.13ve Python ile test edildi3.5.3

  • Python 2.x'de .iteritems(),.items()


1

Yakın zamanda bu soruya benzer bir şey bulmam gerekti, bu yüzden gelecekte aynı şeyle karşılaşan diğerlerine yardımcı olabilecek bazı arka plan bilgileri göndermek istedim.

Python'da şu şekilde çalışır ( https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy adresinden ):

MyClassbir sınıf nesnesidir, sınıf nesnesinin MyClass()bir örneğidir. Bir örneğin __dict__yalnızca bu örneğe özgü hold nitelikleri ve yöntemleri (örneğin self.somethings). Bir öznitelik veya yöntem bir sınıfın parçasıysa, sınıfın __dict__. Bunu yaptığınızda , sınıf özniteliklerinin yanı sıra öznitelik veya yöntem içermeyen MyClass().__dict__bir örneği MyClassoluşturulur, bu nedenle boş__dict__

Yani, diyorsan print(MyClass().b), Python önce yeni örneğin diktesini kontrol eder MyClass().__dict__['b']ve bulamaz b. Daha sonra sınıfı kontrol eder MyClass.__dict__['b']ve bulur b.

Bu nedenle inspect, aynı arama sürecini taklit etmek için modüle ihtiyacınız var .


2
Scott - Cevap olarak gönderilen bir yorum silinmelidir, aksi takdirde içinde boğuluruz. Bununla birlikte, bir çözüme yönelik kısmi bir cevap veya "yardımcı dürtü" yine de bir cevaptır . Gönderinizi nasıl değiştirdiğimi göreceksiniz; umarım niyetini korudum. Değilse, şekle daha fazla düzenleyebilirsiniz . Şerefe!
Mogsdad

1

Sen kullanabilirsiniz dir()bir de liste anlama özellik adlarını almak için:

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

getattr()Öznitelikleri kendileri almak için kullanın :

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

1

Bir sınıfın tüm özniteliklerini (yöntemleri değil) elde etmek için benim çözümüm (eğer sınıf, öznitelikleri açıkça belirtilmiş düzgün bir şekilde yazılmış bir docstring'e sahipse):

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

Bu parça cls.__dict__['__doc__']sınıfın doktrini çıkarır.


1

Öznitelikleri neden listelemeniz gerekiyor? Anlamsal olarak sınıfınız bir koleksiyon gibi görünüyor. Bu durumlarda enum kullanmanızı tavsiye ederim:

import enum

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

Niteliklerinizi listeleyin? Bundan daha kolay bir şey yok:

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

1

Bir özniteliği "elde etmek" istiyorsanız, çok basit bir yanıt vardır ve bu açık olmalıdır: 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>

Hem Python 2.7 hem de Python 3.x'te mükemmel çalışır.

Bu öğelerin bir listesini istiyorsanız, yine de inspect kullanmanız gerekecektir.


1
Bu cevap çok basit ve herhangi bir noktayı hak etmek için fazla doğru mu ve hatta kötü noktaları hak etmeli mi? Görünüşe göre ekonomi ve basitlik günümüzde artık işe yaramıyor.
fralau

0

iki işlev:

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

kullanım:

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

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

0

İstediğim şey şu.

Test verisi

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

-2

Bunun üç yıl önce olduğunu biliyorum, ama gelecekte bu soruyla gelecek olanlar için, benim için:

class_name.attribute 

gayet iyi çalışıyor.


3
AttributeError aldığınız durumlar hariç.
rady

attributeÖnceden ne olduğunu her zaman bilemezsiniz .
Matt Luongo

-3

Kullanabilirsiniz MyClass.__attrs__. Sadece o sınıfın tüm özelliklerini verir. Daha fazlası yok.


AttributeError: 'X' tür nesnesinin ' attrs ' niteliği yok
Ramazan Polat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.