Obtener atributos de una clase

106

Quiero obtener los atributos de una clase, digamos:

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

  def myfunc(self):
    return self.a

el uso MyClass.__dict__me da una lista de atributos y funciones, e incluso funciones como __module__y __doc__. Mientras MyClass().__dict__me da un dict vacío a menos que establezca explícitamente un valor de atributo de esa instancia.

Solo quiero los atributos, en el ejemplo anterior serían: ayb

Mohamed Khamis
fuente
posible duplicado de los atributos de la clase Inspect python
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

123

Pruebe el módulo de inspección . getmembersy las diversas pruebas deberían ser útiles.

EDITAR:

Por ejemplo,

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

Ahora, los métodos y atributos especiales me ponen de los nervios; se pueden tratar de varias maneras, la más fácil de las cuales es simplemente filtrar según el nombre.

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

... y el más complicado de los cuales puede incluir comprobaciones de nombres de atributos especiales o incluso metaclases;)

Matt Luongo
fuente
¡Sí, esto es genial! Usé esto: 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
Tenga cuidado, ¡eso no incluirá atributos like_this! También evitará los atributos "privados", lo que podría haber hecho a propósito.
Matt Luongo
Hola, me encantó eso también con una ligera aclaración: en la expresión inspect.getmembers(MyClass, ..., MyClasspuede ser reemplazado por una clase o un objeto, y si necesitas la lista de los valores de tus objetos, debes reemplazarlo MyClasspor tu variable de objeto (o selfsi pones esta expresión en un def __repr__()método como yo).
herve-guerin
Usé esto (en Python3) para obtener una función que buscara el valor ' dict ': i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]y luego solo es cuestión de obtener las claves de z.
double0darbo
43
def props(cls):   
  return [i for i in cls.__dict__.keys() if i[:1] != '_']

properties = props(MyClass)
Doug
fuente
7
Esto incluirá nombres de métodos
lenhhoxung
10
¿No sería más claro comprobar: en if not i.startswith('_')lugar de if i[:1] != '_'?
Mikaelblomkvistsson
2
Nota: si hablamos de una clase secundaria (heredada), entonces .__dict__.keys()no incluiremos los atributos de los padres.
vishes_shell
21

myfunc es un atributo de MyClass. Así es como se encuentra cuando corres:

myinstance = MyClass()
myinstance.myfunc()

Busca un atributo en myinstancenamed myfunc, no encuentra uno, ve que myinstancees una instancia de MyClassy lo busca allí.

Entonces, la lista completa de atributos para MyClasses:

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

(Tenga en cuenta que estoy usando dir solo como una forma rápida y fácil de enumerar los miembros de la clase: solo debe usarse de manera exploratoria, no en el código de producción)

Si sólo desea atributos particulares, tendrá que filtrar esta lista con algunos criterios, porque __doc__, __module__y myfuncno son de ninguna manera especial, son atributos exactamente de la misma manera que ay bson.

Nunca he usado el módulo de inspección al que se refieren Matt y Borealid, pero a partir de un breve enlace, parece que tiene pruebas para ayudarlo a hacer esto, pero deberá escribir su propia función de predicado, ya que parece lo que quiere son aproximadamente los atributos que no pasan la isroutineprueba y no comienzan ni terminan con dos guiones bajos.

También tenga en cuenta: al usar class MyClass():Python 2.7, está usando las clases de estilo antiguo muy desactualizadas. A menos que lo esté haciendo deliberadamente por compatibilidad con bibliotecas extremadamente antiguas, debería definir su clase como class MyClass(object):. En Python 3 no hay clases de "estilo antiguo" y este comportamiento es el predeterminado. Sin embargo, el uso de clases de estilo nuevo le proporcionará muchos más atributos definidos automáticamente:

>>> 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
fuente
6
No se puede depender de dir(): " Debido a que dir () se proporciona principalmente como una conveniencia para su uso en un indicador interactivo, intenta proporcionar un conjunto interesante de nombres más de lo que intenta proporcionar un conjunto de nombres definido de manera rigurosa o coherente , y su comportamiento puede cambiar a través de comunicados . "(véase la documentación deldir() ).
Tadeck
@Tadeck: Buen punto. Lo estaba usando de manera ilustrativa en lugar de sugerirlo como una solución, ya que no le permitiría filtrar fácilmente los atributos en función de a qué se refieren. Pero debería ser más explícito al respecto.
Ben
15

Obtener solo los atributos de la instancia es fácil.
Pero obtener también los atributos de clase sin las funciones es un poco más complicado.

Solo atributos de instancia

Si solo tiene que enumerar los atributos de instancia, use
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 instancia y clase

Para obtener también los atributos de clase sin las funciones, el truco consiste en usar callable().

¡Pero los métodos estáticos no siempre locallable son !

Por lo tanto, en lugar de usar callable(value)use
callable( getattr(MyClass, attribute))

Ejemplo

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() debería ser,       pero no en este ejemplo simple y estúpido .@staticmethod

Resultado para

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

Mismo resultado para

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

MyClass().__class__.__dict__

Sin embargo, el "derecho" era hacer esto a través del módulo de inspección .

Borealido
fuente
6
MyClass().__class__.__dict__==MyClass.__dict__
yak
5
El comentario de @yak no es del todo cierto. Consulte lo siguiente sobre las diferencias entre los atributos de clase e instancia. Consulte stackoverflow.com/questions/35805/… .
sholsapp
@sholsapp en realidad @yak tiene razón. El enlace que proporcionó dice eso MyClass().__class__.__dict__ != MyClass().__dict__, pero yak no incluye los paréntesis en el lado derecho, en el caso de que él / ella esté en lo correcto
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 una instancia de MyClass, como

mc = MyClass()

utilizar type(mc)en lugar de MyClassen la comprensión de la lista. Sin embargo, si uno agrega dinámicamente un atributo mc, como mc.c = "42", el atributo no aparecerá cuando se use type(mc)en esta estrategia. Solo da los atributos de la clase original.

Para obtener el diccionario completo para una instancia de clase, necesitaría COMBINAR los diccionarios de type(mc).__dict__ y 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
fuente
Solución realmente ordenada.
Gitnik
2

No sé si se ha hecho algo similar a esta altura o no, pero hice una buena función de búsqueda de atributos usando vars (). vars () crea un diccionario de los atributos de una clase que pasa por él.

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

Estoy desarrollando una aventura de texto bastante masiva en Python, mi clase Player hasta ahora tiene más de 100 atributos. Utilizo esto para buscar atributos específicos que necesito ver.

Corey Bailey
fuente
Desafortunadamente, vars () no devolverá los atributos de clase
user2682863
¿Ha intentado ejecutar el código que publiqué? Vars definitivamente puede devolver atributos de clase. Muéstrame un ejemplo de cómo no es así. Quizás mi código sea incorrecto. Pero asignando vars () a una variable y usando una clave, la búsqueda de valor a través de esa variable puede devolver atributos de clase.
Corey Bailey
clase T: x = 1; t = T (); vars (t)
user2682863
Tendrás que esperar hasta que salga del trabajo para mostrarte como es debido. Pero tu código es incorrecto. Su objeto de clase debe definir __init __ (self) y x debe ser self.x = 1. Luego asigne t = T () y use print (vars (t)) y le mostrará un diccionario de todos los atributos de la clase.
Corey Bailey
no, esos son atributos de instancia, no atributos de clase, muchas subclases nunca llaman a init . Como dije, vars () no devolverá atributos de clase, debe usar dir () o inspect.getmembers ()
user2682863
2

Esto se puede hacer sin inspeccionar, supongo.

Toma la siguiente clase:

 class Test:
   a = 1
   b = 2

   def __init__(self):
     self.c = 42

   @staticmethod
   def toto():
     return "toto"

   def test(self):
     return "test"

Mirando a los miembros junto con sus tipos:

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

... da:

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

Entonces, para generar solo las variables, solo tiene que filtrar los resultados por tipo y nombres que no comiencen con '__'. P.ej

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

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

Eso es.

Nota: si está utilizando Python 3, convierta los iteradores en listas.

Si desea una forma más sólida de hacerlo, use inspeccionar .

carmelosa
fuente
2

Python 2 y 3, sin importaciones, filtrando objetos por su dirección

Soluciones en resumen:

Devuelve dict {attribute_name: attribute_value} , objetos filtrados. es decir{'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 devolución [nombre_atributo] , objetos filtrados. es decir['a', 'b', 'c', 'd']

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

Lista de devolución [atributo_valores] , objetos filtrados. es decir[1, (2, 2), [3, 3], {4: 4}]

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

Sin filtrar objetos

Eliminando la ifcondición. Regreso{'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()}

Solución en largo

Siempre que __repr__no se anule la implementación predeterminada de, la ifdeclaración regresará Truesi la representación hexadecimal de la ubicación en la memoria de valestá en la __repr__cadena de retorno.

Con respecto a la implementación predeterminada de __repr__, podría encontrar útil esta respuesta . En breve:

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

Que devuelve una cadena como:

<__main__.Bar object at 0x7f3373be5998>

La ubicación en la memoria de cada elemento se obtiene mediante el id()método.

Python Docs dice sobre id ():

Devuelve la "identidad" de un objeto. Este es un número entero que se garantiza que es único y constante para este objeto durante su vida útil. Dos objetos con vidas útiles que no se superponen pueden tener el mismo valor de id ().

Detalle de implementación de CPython: esta es la dirección del objeto en la memoria.


Pruébelo usted mismo

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

Salida:

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

Nota :

  • Probado con Python 2.7.13y Python3.5.3

  • En Python 2.x .iteritems()se prefiere a.items()

Marco DG
fuente
1

Recientemente, necesitaba resolver algo similar a esta pregunta, por lo que quería publicar información de fondo que podría ser útil para otras personas que enfrentan lo mismo en el futuro.

Así es como funciona en Python (de https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy ):

MyClasses un objeto de clase, MyClass()es una instancia del objeto de clase. Una instancia __dict__solo contiene atributos y métodos específicos de esa instancia (p self.somethings. Ej .). Si un atributo o método es parte de una clase, está en el de la clase __dict__. Cuando lo hace MyClass().__dict__, una instancia deMyClass se crea sin atributos o métodos además de los atributos de clase, por lo que el vacío__dict__

Entonces, si dice print(MyClass().b), Python primero verifica el dict de la nueva instancia MyClass().__dict__['b']y no encuentra b. Luego verifica la clase MyClass.__dict__['b']y encuentra b.

Por eso necesita el inspectmódulo, para emular ese mismo proceso de búsqueda.

Scott Howard
fuente
2
Scott: un comentario publicado como respuesta debe eliminarse, de lo contrario nos ahogaríamos en ellos. Sin embargo, una respuesta parcial o un "empujón útil" hacia una solución sigue siendo una respuesta . Verá cómo reformulé su publicación; Ojalá mantuviera tu intención. De lo contrario, puede editarlo aún más para darle forma. ¡Salud!
Mogsdad
1

Puede usar dir()en una lista de comprensión para obtener los nombres de los atributos:

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

Úselo getattr()para obtener los atributos en sí mismos:

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

Mi solución para obtener todos los atributos (no métodos) de una clase (si la clase tiene una cadena de documentación escrita correctamente que tiene los atributos claramente detallados):

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

Esta pieza cls.__dict__['__doc__']extrae la cadena de documentación de la clase.

Henry en
fuente
1

¿Por qué necesita enumerar los atributos? Parece que semánticamente tu clase es una colección. En estos casos, recomiendo usar enum:

import enum

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

Enumere sus atributos? Nada más fácil que esto:

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

Si desea "obtener" un atributo, hay una respuesta muy simple , que debería ser obvia: 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 a la perfección tanto en Python 2.7 como en Python 3.x.

Si desea una lista de estos elementos, aún deberá utilizar inspeccionar.

fralau
fuente
1
¿Es esa respuesta demasiado simple y demasiado correcta para merecer algún punto, e incluso debería merecer puntos negativos? Parece que la economía y la sencillez ya no dan sus frutos hoy en día.
fralau
0

dos funciones:

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

utilizar:

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

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

Lo siguiente es lo que quiero.

Datos de prueba

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

Sé que esto fue hace tres años, pero para aquellos que vendrán con esta pregunta en el futuro, para mí:

class_name.attribute 

funciona bien.

Jim Jam
fuente
3
excepto cuando obtiene un AttributeError.
Rady
No siempre sabes lo que attributees de antemano.
Matt Luongo
-3

Puede utilizar MyClass.__attrs__. Simplemente da todos los atributos de esa clase. Nada mas.

jimxliu
fuente
AttributeError: el objeto de tipo 'X' no tiene el atributo ' attrs '
Ramazan Polat