Ottenere gli attributi di una classe


106

Voglio ottenere gli attributi di una classe, ad esempio:

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

  def myfunc(self):
    return self.a

using MyClass.__dict__mi fornisce un elenco di attributi e funzioni e persino funzioni come __module__e __doc__. Mentre MyClass().__dict__mi dà un dict vuoto a meno che non abbia impostato esplicitamente un valore di attributo di quell'istanza.

Voglio solo gli attributi, nell'esempio sopra quelli sarebbero: aeb


Risposte:


123

Prova il modulo di ispezione . getmemberse i vari test dovrebbero essere utili.

MODIFICARE:

Per esempio,

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

Ora, i metodi e gli attributi speciali mi danno sui nervi: possono essere gestiti in diversi modi, il più semplice dei quali è semplicemente quello di filtrare in base al nome.

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

... e il più complicato dei quali può includere controlli speciali del nome dell'attributo o anche metaclassi;)


sì, è fantastico! Ho usato questo: 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
Fai attenzione: non includerà attributi like_this! Eviterà anche attributi "privati", che potresti aver fatto apposta.
Matt Luongo

Ciao, mi è piaciuto anche questo con una piccola precisazione: nell'espressione inspect.getmembers(MyClass, ..., MyClasspuò essere sostituito da una classe o da un oggetto, e se hai bisogno della lista dei valori dei tuoi oggetti, devi sostituirlo MyClasscon la tua variabile oggetto (o selfse metti questa espressione in un def __repr__()metodo come me).
herve-guerin

Ho usato questo (in Python3) per ottenere una funzione che cercava il valore ' dict ': i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]e quindi è solo questione di ottenere le chiavi da z.
double0darbo

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

properties = props(MyClass)

7
Ciò includerà i nomi dei metodi
lenhhoxung

10
Non sarebbe più chiaro controllare: if not i.startswith('_')invece di if i[:1] != '_'?
Mikaelblomkvistsson

2
Nota: se parliamo di classe figlia (ereditata), .__dict__.keys()non includeremo attributi dal genitore.
vishes_shell

21

myfunc è un attributo di MyClass. Ecco come si trova quando corri:

myinstance = MyClass()
myinstance.myfunc()

Cerca un attributo su myinstancenamed myfunc, non ne trova uno, vede che myinstanceè un'istanza di MyClasse lo cerca lì.

Quindi l' elenco completo degli attributi per MyClassè:

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

(Nota che sto usando dir solo come un modo semplice e veloce per elencare i membri della classe: dovrebbe essere usato solo in modo esplorativo, non nel codice di produzione)

Se si desidera solo particolari attributi, è necessario filtrare questo elenco utilizzando alcuni criteri, perché __doc__, __module__e myfuncnon sono in alcun modo speciale, sono gli attributi esattamente nello stesso modo in cui ae bsono.

Non ho mai usato il modulo inspect a cui fanno riferimento Matt e Borealid, ma da un breve collegamento sembra che abbia dei test per aiutarti a farlo, ma dovrai scrivere la tua funzione predicato, poiché sembra quello che vuoi sono più o meno gli attributi che non superano il isroutinetest e non iniziano e finiscono con due trattini bassi.

Nota anche: usando class MyClass():in Python 2.7 stai usando le classi vecchio stile selvaggiamente obsolete. A meno che tu non lo stia facendo deliberatamente per compatibilità con librerie estremamente vecchie, dovresti invece definire la tua classe come class MyClass(object):. In Python 3 non ci sono classi "vecchio stile" e questo comportamento è quello predefinito. Tuttavia, l'utilizzo delle classi newstyle ti consentirà di ottenere molti più attributi definiti automaticamente:

>>> 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
Non si può dipendere da dir(): " Poiché dir () è fornito principalmente come comodità per l'uso a un prompt interattivo, cerca di fornire un insieme interessante di nomi più di quanto cerchi di fornire un insieme di nomi rigorosamente o coerentemente definito , e il suo dettaglio il comportamento può cambiare tra le versioni . "(vedere la documentazione didir() ).
Tadeck

@Tadeck: buon punto. Lo stavo usando in modo illustrativo piuttosto che suggerirlo come soluzione, poiché non ti consentirebbe facilmente di filtrare gli attributi in base a ciò a cui si riferiscono. Ma dovrei essere più esplicito al riguardo.
Ben

15

Ottenere solo gli attributi dell'istanza è facile.
Ma ottenere anche gli attributi di classe senza le funzioni è un po 'più complicato.

Solo attributi di istanza

Se devi solo elencare gli attributi dell'istanza, usa
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

Istanza e attributi di classe

Per ottenere anche gli attributi della classe senza le funzioni, il trucco è usare callable().

Ma i metodi statici noncallable sono sempre !

Pertanto, invece di usare callable(value)use
callable( getattr(MyClass, attribute))

Esempio

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

Nota: print_class_attributes() dovrebbe essere       ma non in questo stupido e semplice esempio.@staticmethod

Risultato per

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

Stesso risultato per

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

8

MyClass().__class__.__dict__

Tuttavia, il "diritto" era farlo tramite il modulo di ispezione .


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

5
Il commento di @ yak non è del tutto vero. Vedere quanto segue sulle differenze tra attributi di classe e istanza. Vedi stackoverflow.com/questions/35805/… .
sholsapp

@sholsapp in realtà @yak ha ragione. Il link che hai fornito dice che MyClass().__class__.__dict__ != MyClass().__dict__, ma yak non include le parentesi sul lato destro, nel caso in cui lui / lei è corretto
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('__'))]

Per un'istanza di MyClass, come

mc = MyClass()

utilizzare type(mc)al posto di MyClassnella lista di comprensione. Tuttavia, se si aggiunge dinamicamente un attributo a mc, ad esempio mc.c = "42", l'attributo non verrà visualizzato quando si utilizza type(mc)in questa strategia. Fornisce solo gli attributi della classe originale.

Per ottenere il dizionario completo per un'istanza di classe, è necessario COMBINARE i dizionari di type(mc).__dict__e 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('__'))]

Soluzione davvero accurata.
Gitnik

2

Non so se sia stato fatto qualcosa di simile o no, ma ho creato una bella funzione di ricerca degli attributi usando vars (). vars () crea un dizionario degli attributi di una classe che lo attraversi.

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

Sto sviluppando un'avventura testuale piuttosto imponente in Python, la mia classe Player finora ha oltre 100 attributi. Lo uso per cercare attributi specifici che devo vedere.


sfortunatamente vars () non restituirà gli attributi della classe
user2682863

Hai provato a eseguire il codice che ho pubblicato? Vars può sicuramente restituire attributi di classe. Mostrami un esempio di come non funziona? Forse il mio codice non è corretto. Ma l'assegnazione di vars () a una variabile e l'utilizzo di una chiave, la ricerca di valori attraverso quella variabile può restituire attributi di classe.
Corey Bailey

classe T: x = 1; t = T (); vars (t)
user2682863

Dovrai aspettare che esca dal lavoro per mostrartelo correttamente. Ma il tuo codice non è corretto. Il tuo oggetto classe deve definire __init __ (self) e x deve essere self.x = 1. Quindi assegna t = T () e usa print (vars (t)) e ti mostrerà un dizionario di tutti gli attributi della classe.
Corey Bailey

no, quelli sono attributi di istanza non attributi di classe, molte sottoclassi non chiamano mai init . Come ho detto, vars () non restituirà attributi di classe, è necessario utilizzare dir () o inspect.getmembers ()
user2682863

2

Questo può essere fatto senza ispezione, immagino.

Segui il seguente corso:

 class Test:
   a = 1
   b = 2

   def __init__(self):
     self.c = 42

   @staticmethod
   def toto():
     return "toto"

   def test(self):
     return "test"

Guardando i membri insieme ai loro tipi:

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

... dà:

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

Quindi, per visualizzare solo le variabili, devi solo filtrare i risultati per tipo e nomi che non iniziano con "__". Per esempio

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

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

Questo è tutto.

Nota: se stai usando Python 3, converti gli iteratori in elenchi.

Se vuoi un modo più robusto per farlo, usa inspect .


2

Python 2 e 3, senza importazioni, filtrando gli oggetti in base al loro indirizzo

Soluzioni in breve:

Restituisce dict {nome_attributo: valore_attributo} , oggetti filtrati. vale a dire{'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)}

Elenco di restituzione [nome_attributo] , oggetti filtrati. vale a dire['a', 'b', 'c', 'd']

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

Elenco di restituzione [valori_attributo] , oggetti filtrati. vale a dire[1, (2, 2), [3, 3], {4: 4}]

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

Non filtra gli oggetti

Rimozione della ifcondizione. Ritorno{'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()}

Soluzione a lungo

Fintanto che l'implementazione predefinita di __repr__non è sovrascritta, l' ifistruzione restituirà Truese la rappresentazione esadecimale della posizione in memoria di valè nella __repr__stringa di ritorno.

Per quanto riguarda l'implementazione predefinita di __repr__te potrebbe trovare utile questa risposta . In breve:

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

Che restituisce una stringa come:

<__main__.Bar object at 0x7f3373be5998>

La posizione in memoria di ogni elemento viene ottenuta tramite il id()metodo.

Python Docs dice di id ():

Restituisce l '“identità” di un oggetto. Questo è un numero intero che è garantito per essere unico e costante per questo oggetto durante la sua vita. Due oggetti con durate non sovrapposte possono avere lo stesso valore id ().

Dettagli sull'implementazione di CPython: questo è l'indirizzo dell'oggetto in memoria.


Prova da solo

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

Produzione:

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

Nota :

  • Testato con Python 2.7.13e Python3.5.3

  • In Python 2.x .iteritems()è preferito rispetto a.items()


1

Recentemente avevo bisogno di capire qualcosa di simile a questa domanda, quindi ho voluto pubblicare alcune informazioni di base che potrebbero essere utili ad altri che affrontano la stessa cosa in futuro.

Ecco come funziona in Python (da https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy ):

MyClassè un oggetto di classe, MyClass()è un'istanza dell'oggetto di classe. Un'istanza __dict__contiene solo attributi e metodi specifici per quell'istanza (ad esempio self.somethings). Se un attributo o un metodo fa parte di una classe, è nella classe __dict__. Quando lo fai MyClass().__dict__, MyClassviene creata un'istanza di senza attributi o metodi oltre agli attributi di classe, quindi il vuoto__dict__

Quindi, se dici print(MyClass().b), Python controlla prima il dict della nuova istanza MyClass().__dict__['b']e non riesce a trovarlo b. Quindi controlla la classe MyClass.__dict__['b']e trova b.

Ecco perché hai bisogno del inspectmodulo, per emulare lo stesso processo di ricerca.


2
Scott - Un commento pubblicato come risposta deve essere cancellato, altrimenti annegheremmo in loro. Tuttavia, una risposta parziale o una "spinta utile" verso una soluzione è ancora una risposta . Vedrai come ho riformulato il tuo post; spero di aver mantenuto il tuo intento. In caso contrario, puoi modificarlo ulteriormente nella forma. Saluti!
Mogsdad

1

È possibile utilizzare dir()in una comprensione dell'elenco per ottenere i nomi degli attributi:

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

Utilizzare getattr()per ottenere gli attributi stessi:

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

1

La mia soluzione per ottenere tutti gli attributi (non i metodi) di una classe (se la classe ha una docstring scritta correttamente che ha gli attributi chiaramente enunciati):

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

Questo pezzo cls.__dict__['__doc__']estrae la docstring della classe.


1

Perché devi elencare gli attributi? Sembra che semanticamente la tua classe sia una raccolta. In questi casi consiglio di usare enum:

import enum

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

Elenca i tuoi attributi? Niente di più facile di questo:

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

1

Se vuoi "ottenere" un attributo, c'è una risposta molto semplice , che dovrebbe essere ovvia: 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>

Funziona alla grande sia in Python 2.7 che in Python 3.x.

Se desideri un elenco di questi elementi, dovrai comunque utilizzare inspect.


1
Questa risposta è troppo semplice e troppo corretta per meritare punti, e dovrebbe anche meritare punti negativi? Sembra che l'economia e la semplicità non ripagino più oggi.
fralau

0

due funzioni:

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

uso:

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

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

0

Quello che segue è quello che voglio.

Dati di test

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

So che è stato tre anni fa, ma per coloro che verranno da questa domanda in futuro, per me:

class_name.attribute 

funziona bene.


3
tranne quando ottieni un AttributeError.
rady

Non sai sempre cosa attributec'è in anticipo.
Matt Luongo

-3

Puoi usare MyClass.__attrs__. Fornisce solo tutti gli attributi di quella classe. Niente di più.


AttributeError: il tipo oggetto "X" non ha attributo " attrs "
Ramazan Polat
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.