Obtendo atributos de uma classe

106

Quero obter os atributos de uma classe, diga:

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

  def myfunc(self):
    return self.a

usando MyClass.__dict__me dá uma lista de atributos e funções, e até funções como __module__e __doc__. Enquanto MyClass().__dict__me dá um dicionário vazio, a menos que eu defina explicitamente um valor de atributo dessa instância.

Eu só quero os atributos, no exemplo acima, eles seriam: aeb

Mohamed Khamis
fonte
possível duplicata dos atributos de classe Inspecionar Python
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respostas:

123

Experimente o módulo de inspeção . getmemberse os vários testes devem ser úteis.

EDITAR:

Por exemplo,

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

Agora, os métodos e atributos especiais me dão nos nervos - eles podem ser tratados de várias maneiras, a mais fácil das quais é apenas filtrar com base no 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 o mais complicado dos quais pode incluir verificações de nomes de atributos especiais ou mesmo metaclasses;)

Matt Luongo
fonte
sim, isso é ótimo! Eu usei isto: 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
Tenha cuidado - isso não incluirá atributos like_this! Isso também evitará atributos "privados", o que você pode ter feito de propósito.
Matt Luongo
Olá, adorei isso também com um pequeno esclarecimento: na expressão inspect.getmembers(MyClass, ..., MyClasspode ser substituído por uma classe ou um objeto, e se você precisar da lista dos valores de seus objetos, deve substituir MyClasspor sua variável de objeto (ou selfse você colocar esta expressão em um def __repr__()método como eu).
herve-guerin
Usei isso (em Python3) para obter uma função que procurava o valor ' dict ': i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]e então é apenas uma questão de obter as chaves de z.
double0darbo
43
def props(cls):   
  return [i for i in cls.__dict__.keys() if i[:1] != '_']

properties = props(MyClass)
Doug
fonte
7
Isso incluirá nomes de métodos
lenhhoxung
10
Não seria mais claro verificar: em if not i.startswith('_')vez de if i[:1] != '_'?
Mikaelblomkvistsson
2
Nota: se falamos sobre classe filho (herdado), então .__dict__.keys()não incluiremos atributos do pai.
vishes_shell
21

myfunc é um atributo de MyClass. É assim que ele é encontrado quando você executa:

myinstance = MyClass()
myinstance.myfunc()

Ele procura por um atributo no myinstancenamed myfunc, não encontra nenhum, vê que myinstanceé uma instância de MyClasse procura lá em cima.

Portanto, a lista completa de atributos para MyClassé:

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

(Observe que estou usando dir apenas como uma maneira rápida e fácil de listar os membros da classe: ele deve ser usado apenas de forma exploratória, não em código de produção)

Se você só quer atributos particulares, você precisa filtrar essa lista usando alguns critérios, pois __doc__, __module__e myfuncnão são especiais de alguma forma, eles são atributos exatamente da mesma forma que ae bsão.

Eu nunca usei o módulo inspect referido por Matt e Borealid, mas a partir de um breve link parece que ele tem testes para ajudá-lo a fazer isso, mas você precisará escrever sua própria função de predicado, uma vez que parece o que você deseja são aproximadamente os atributos que não passam no isroutineteste e não começam e terminam com dois sublinhados.

Observe também: ao usar class MyClass():no Python 2.7, você está usando as classes de estilo antigo totalmente desatualizadas. A menos que você esteja fazendo isso deliberadamente para compatibilidade com bibliotecas extremamente antigas, você deve definir sua classe como class MyClass(object):. No Python 3 não há classes "no estilo antigo" e esse comportamento é o padrão. No entanto, o uso de classes de estilo novo resultará em muito mais atributos definidos 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']
Ben
fonte
6
Não se pode depender de dir(): " Como dir () é fornecido principalmente como uma conveniência para uso em um prompt interativo, ele tenta fornecer um conjunto interessante de nomes mais do que um conjunto de nomes definido de forma rigorosa ou consistente , e seus detalhes o comportamento pode mudar entre as versões . "(consulte a documentação dodir() ).
Tadeck
@Tadeck: Bom ponto. Eu estava usando isso de forma ilustrativa, em vez de sugerir como solução, já que não permitiria facilmente filtrar os atributos com base no que eles se referem. Mas eu deveria ser mais explícito sobre isso.
Ben
14

Obter apenas os atributos da instância é fácil.
Mas obter também os atributos de classe sem as funções é um pouco mais complicado.

Atributos de instância apenas

Se você só precisa listar os atributos de instância, basta usar
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

Atributos de instância e classe

Para obter também os atributos de classe sem as funções, o truque é usar callable().

Mas os métodos estáticos nem semprecallable são !

Portanto, em vez de usar callable(value)use
callable( getattr(MyClass, attribute))

Exemplo

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() deveria ser,       mas não neste exemplo estúpido e simples .@staticmethod

Resultado para

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

Mesmo resultado para

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

MyClass().__class__.__dict__

No entanto, o "certo" era fazer isso por meio do módulo de inspeção .

Borealida
fonte
6
MyClass().__class__.__dict__==MyClass.__dict__
yak
5
O comentário de @yak não é bem verdade. Veja a seguir as diferenças entre os atributos de classe e instância. Consulte stackoverflow.com/questions/35805/… .
sholsapp
@sholsapp, na verdade, @yak está certo. O link que você forneceu diz isso MyClass().__class__.__dict__ != MyClass().__dict__, mas yak não está incluindo os parênteses do lado direito, caso esteja correto
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('__'))]

Para uma instância de MyClass, como

mc = MyClass()

use type(mc)no lugar de MyClassna lista de compreensão. No entanto, se alguém adicionar dinamicamente um atributo a mc, como mc.c = "42", o atributo não aparecerá quando usado type(mc)nesta estratégia. Ele apenas fornece os atributos da classe original.

Para obter o dicionário completo para uma instância de classe, você precisaria COMBINAR os dicionários de 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('__'))]
JD Graham
fonte
Solução realmente legal.
Gitnik
2

Não sei se algo semelhante foi feito até agora ou não, mas fiz uma boa função de pesquisa de atributos usando vars (). vars () cria um dicionário dos atributos de uma classe que você passa por ele.

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

Estou desenvolvendo uma grande aventura de texto em Python, minha classe Player até agora tem mais de 100 atributos. Eu uso isso para pesquisar atributos específicos que preciso ver.

Corey Bailey
fonte
infelizmente vars () não retornará atributos de classe
user2682863
Você já tentou executar o código que postei? Vars podem definitivamente retornar atributos de classe. Mostre-me um exemplo de como isso não acontece? Talvez meu código esteja incorreto. Mas, atribuindo vars () a uma variável e usando uma chave, a pesquisa de valor por meio dessa variável pode retornar atributos de classe.
Corey Bailey
classe T: x = 1; t = T (); vars (t)
user2682863
Você vai ter que esperar até eu sair do trabalho para mostrar a você adequadamente. Mas seu código está incorreto. Seu objeto de classe precisa definir __init __ (self) e x precisa ser self.x = 1. Então atribua t = T () e use print (vars (t)) e ele mostrará a você um dicionário de todos os atributos da classe.
Corey Bailey
não, esses são atributos de instância, não atributos de classe, muitas subclasses nunca chamam init . Como eu disse, vars () não retornará atributos de classe, você precisa usar dir () ou inspect.getmembers ()
user2682863
2

Isso pode ser feito sem inspeção, eu acho.

Faça a seguinte aula:

 class Test:
   a = 1
   b = 2

   def __init__(self):
     self.c = 42

   @staticmethod
   def toto():
     return "toto"

   def test(self):
     return "test"

Olhando para os membros junto com seus tipos:

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

Portanto, para gerar apenas as variáveis, você só precisa filtrar os resultados por tipo e nomes que não começam com '__'. Por exemplo

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

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

É isso aí.

Nota: se você estiver usando Python 3, converta os iteradores em listas.

Se você quiser uma maneira mais robusta de fazer isso, use inspect .

carmelose
fonte
2

Python 2 e 3, sem importações, filtrando objetos por seus endereços

Soluções resumidas:

Retorne dict {attribute_name: attribute_value} , objetos filtrados. ie{'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)}

Lista de retorno [attribute_names] , objetos filtrados. ie['a', 'b', 'c', 'd']

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

Lista de retorno [attribute_values] , objetos filtrados. ie[1, (2, 2), [3, 3], {4: 4}]

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

Não filtrando objetos

Removendo a ifcondição. Retorna{'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()}

Solução em longo prazo

Desde que a implementação padrão de __repr__não seja substituída, a ifinstrução retornará Truese a representação hexadecimal do local na memória de valestiver na __repr__string de retorno.

Em relação à implementação padrão de, __repr__você pode achar útil esta resposta . Em resumo:

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

Que retorna uma string como:

<__main__.Bar object at 0x7f3373be5998>

A localização na memória de cada elemento é obtida por meio do id()método.

Python Docs diz sobre id ():

Retorna a “identidade” de um objeto. Este é um número inteiro garantido como único e constante para este objeto durante sua vida útil. Dois objetos com tempos de vida não sobrepostos podem ter o mesmo valor id ().

Detalhe de implementação do CPython: Este é o endereço do objeto na memória.


Tente você mesmo

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

Resultado:

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

Nota :

  • Testado com Python 2.7.13e Python3.5.3

  • Em Python 2.x .iteritems()é preferível.items()

Marco DG
fonte
1

Recentemente, precisei descobrir algo semelhante a esta questão, então queria postar algumas informações básicas que podem ser úteis para outras pessoas que enfrentem o mesmo no futuro.

Veja como funciona em Python (em https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy ):

MyClassé um objeto de classe, MyClass()é uma instância do objeto de classe. Uma instância __dict__contém apenas atributos e métodos específicos para aquela instância (por exemplo self.somethings). Se um atributo ou método fizer parte de uma classe, está na classe __dict__. Ao fazer isso MyClass().__dict__, uma instância de MyClassé criada sem atributos ou métodos além dos atributos de classe, portanto, o vazio__dict__

Portanto, se você disser print(MyClass().b), Python primeiro verifica o dict da nova instância MyClass().__dict__['b']e não consegue encontrar b. Em seguida, verifica a classe MyClass.__dict__['b']e encontra b.

É por isso que você precisa do inspectmódulo, para emular o mesmo processo de pesquisa.

Scott Howard
fonte
2
Scott - Um comentário postado como uma resposta deve ser excluído, caso contrário, estaríamos nos afogando neles. No entanto, uma resposta parcial ou "empurrão útil" em direção a uma solução ainda é uma resposta . Você verá como eu reformulei sua postagem; espero ter mantido sua intenção. Caso contrário, você pode editá- lo posteriormente na forma. Felicidades!
Mogsdad,
1

Você pode usar dir()em uma compreensão de lista para obter os nomes dos atributos:

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

Use getattr()para obter os próprios atributos:

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

Minha solução para obter todos os atributos (não métodos) de uma classe (se a classe tiver uma docstring devidamente escrita que tenha os atributos claramente especificados):

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

Esta peça cls.__dict__['__doc__']extrai a docstring da classe.

Henry On
fonte
1

Por que você precisa listar os atributos? Parece que semanticamente sua classe é uma coleção. Nestes casos, recomendo usar enum:

import enum

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

Liste seus atributos? Nada mais fácil do que isso:

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

Se você deseja "obter" um atributo, há uma resposta muito simples , que deveria ser óbvia: 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>

Funciona muito bem no Python 2.7 e no Python 3.x.

Se você quiser uma lista desses itens, ainda precisará usar inspecionar.

Fralau
fonte
1
Essa resposta é muito simples e muito correta para merecer algum ponto, e deveria até mesmo merecer pontos negativos? Parece que economia e simplicidade não compensam mais hoje em dia.
fralau
0

duas funções:

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

usar:

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

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

O seguinte é o que eu quero.

Dados de teste

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
fonte
-2

Eu sei que isso foi há três anos, mas para aqueles que virão por essa questão no futuro, para mim:

class_name.attribute 

funciona muito bem.

Jim Jam
fonte
3
exceto quando você obtém um AttributeError.
rady
Você nem sempre sabe o que attributeé de antemão.
Matt Luongo
-3

Você pode usar MyClass.__attrs__. Ele apenas fornece todos os atributos dessa classe. Nada mais.

Jimxliu
fonte
AttributeError: objeto de tipo 'X' não tem atributo ' attrs '
Ramazan Polat