Obtenir les attributs d'une classe

106

Je veux obtenir les attributs d'une classe, disons:

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

  def myfunc(self):
    return self.a

using MyClass.__dict__me donne une liste d'attributs et de fonctions, et même des fonctions comme __module__et __doc__. While MyClass().__dict__me donne un dict vide à moins que je ne définisse explicitement une valeur d'attribut de cette instance.

Je veux juste les attributs, dans l'exemple ci-dessus, ceux-ci seraient: aetb

Mohamed Khamis
la source
duplication possible des attributs de classe Inspect python
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

123

Essayez le module d' inspection . getmemberset les divers tests devraient être utiles.

ÉDITER:

Par exemple,

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

Maintenant, les méthodes et attributs spéciaux m'énervent - ils peuvent être traités de plusieurs façons, la plus simple étant simplement de filtrer en fonction du nom.

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

... et dont le plus compliqué peut inclure des vérifications de noms d'attributs spéciaux ou même des métaclasses;)

Matt Luongo
la source
ouais c'est super! J'ai utilisé ceci: 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
Attention, cela n'inclura pas les attributs like_this! Cela évitera également les attributs «privés», ce que vous avez peut-être fait exprès.
Matt Luongo
Bonjour, j'ai adoré ça aussi avec une petite précision: dans l'expression inspect.getmembers(MyClass, ..., MyClasspeut être remplacé par une classe ou un objet, et si vous avez besoin de la liste des valeurs de vos objets, vous devez remplacer MyClasspar votre variable objet (ou selfsi vous mettez cette expression dans une def __repr__()méthode comme moi).
herve-guerin
J'ai utilisé ceci (en Python3) pour obtenir une fonction qui recherchait la valeur ' dict ': i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]et ensuite, il s'agit simplement d'obtenir les clés de z.
double0darbo
43
def props(cls):   
  return [i for i in cls.__dict__.keys() if i[:1] != '_']

properties = props(MyClass)
Doug
la source
7
Cela inclura les noms de méthodes
lenhhoxung
10
Ne serait pas plus clair à vérifier: if not i.startswith('_')au lieu de if i[:1] != '_'?
Mikaelblomkvistsson
2
Remarque: si nous parlons de classe enfant (héritée), alors .__dict__.keys()n'inclura pas les attributs du parent.
vishes_shell
21

myfunc est un attribut de MyClass. C'est comme ça que ça se trouve lorsque vous exécutez:

myinstance = MyClass()
myinstance.myfunc()

Il recherche un attribut sur myinstancenamed myfunc, n'en trouve pas, voit qu'il myinstances'agit d'une instance de MyClasset le recherche là-haut.

La liste complète des attributs pour MyClassest donc:

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

(Notez que j'utilise dir simplement comme un moyen rapide et facile de lister les membres de la classe: il ne doit être utilisé que de manière exploratoire, pas dans le code de production)

Si vous ne souhaitez attributs particuliers, vous aurez besoin de filtrer cette liste en utilisant certains critères, parce que __doc__, __module__et myfuncne sont pas particulièrement en aucune façon, ils sont les attributs exactement de la même manière aet bsont.

Je n'ai jamais utilisé le module d'inspection mentionné par Matt et Borealid, mais à partir d'un bref lien, il semble qu'il y ait des tests pour vous aider à le faire, mais vous devrez écrire votre propre fonction de prédicat, car cela semble ce que vous voulez est à peu près les attributs qui ne passent pas le isroutinetest et qui ne commencent et ne se terminent pas par deux traits de soulignement.

Remarquez également: en utilisant class MyClass():Python 2.7, vous utilisez les classes à l'ancienne très obsolètes. À moins que vous ne le fassiez délibérément pour la compatibilité avec des bibliothèques extrêmement anciennes, vous devriez plutôt définir votre classe comme class MyClass(object):. Dans Python 3, il n'y a pas de classes «à l'ancienne», et ce comportement est le comportement par défaut. Cependant, l'utilisation de classes newstyle vous donnera beaucoup plus d'attributs définis automatiquement:

>>> 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
la source
6
On ne peut pas dépendre de dir(): " Parce que dir () est fourni principalement pour une utilisation pratique à une invite interactive, il essaie de fournir un ensemble intéressant de noms plus qu'il n'essaie de fournir un ensemble de noms défini de manière rigoureuse ou cohérente , et ses détails le comportement peut changer d'une version à l'autre . »(voir la documentation dedir() ).
Tadeck
@Tadeck: Bon point. Je l'utilisais à titre illustratif plutôt que de le suggérer comme solution, car cela ne vous permettrait pas facilement de filtrer les attributs en fonction de ce à quoi ils se réfèrent. Mais je devrais être plus explicite à ce sujet.
Ben
16

Obtenir uniquement les attributs de l' instance est facile.
Mais obtenir également les attributs de classe sans les fonctions est un peu plus délicat.

Attributs d'instance uniquement

Si vous n'avez qu'à lister les attributs d'instance, utilisez simplement
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

Attributs d'instance et de classe

Pour obtenir également les attributs de classe sans les fonctions, l'astuce est d'utiliser callable().

Mais les méthodes statiques ne le sont pas toujourscallable !

Par conséquent, au lieu d'utiliser, callable(value)utilisez
callable( getattr(MyClass, attribute))

Exemple

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

Remarque: print_class_attributes() devrait être       mais pas dans cet exemple stupide et simple .@staticmethod

Résultat pour

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

Même résultat pour

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

MyClass().__class__.__dict__

Cependant, le "droit" était de le faire via le module d'inspection .

Boréalide
la source
6
MyClass().__class__.__dict__==MyClass.__dict__
yak
5
Le commentaire de @ yak n'est pas tout à fait vrai. Consultez ce qui suit sur les différences entre les attributs de classe et d'instance. Voir stackoverflow.com/questions/35805/… .
sholsapp
@sholsapp en fait @yak a raison. Le lien que vous avez fourni le dit MyClass().__class__.__dict__ != MyClass().__dict__, mais yak n'inclut pas les parenthèses sur le côté droit, dans le cas où il / elle a raison
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('__'))]

Pour une instance de MyClass, telle que

mc = MyClass()

utiliser type(mc)à la place de MyClassdans la compréhension de la liste. Cependant, si l'on ajoute dynamiquement un attribut à mc, tel que mc.c = "42", l'attribut n'apparaîtra pas lors de l'utilisation type(mc)dans cette stratégie. Il ne donne que les attributs de la classe d'origine.

Pour obtenir le dictionnaire complet d'une instance de classe, vous devez COMBINER les dictionnaires de type(mc).__dict__et 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
la source
Solution vraiment soignée.
Gitnik
2

Je ne sais pas si quelque chose de similaire a déjà été fait ou non, mais j'ai créé une fonction de recherche d'attribut intéressante en utilisant vars (). vars () crée un dictionnaire des attributs d'une classe que vous lui passez.

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

Je développe une aventure textuelle assez massive en Python, ma classe Player a jusqu'à présent plus de 100 attributs. J'utilise ceci pour rechercher des attributs spécifiques que j'ai besoin de voir.

Corey Bailey
la source
Malheureusement, vars () ne retournera pas les attributs de classe
user2682863
Avez-vous essayé d'exécuter le code que j'ai publié? Vars peut certainement renvoyer des attributs de classe. Montre-moi un exemple de comment ça ne marche pas? Peut-être que mon code est incorrect. Mais en assignant vars () à une variable et en utilisant une clé, la recherche de valeur dans cette variable peut renvoyer des attributs de classe.
Corey Bailey
classe T: x = 1; t = T (); vars (t)
user2682863
Vous devrez attendre que je quitte le travail pour vous montrer correctement. Mais votre code est incorrect. Votre objet de classe doit définir __init __ (self) et x doit être self.x = 1. Ensuite, assignez t = T () et utilisez print (vars (t)) et il vous montrera un dictionnaire de tous les attributs de classe.
Corey Bailey
non, ce sont des attributs d'instance et non des attributs de classe, de nombreuses sous-classes n'appellent jamais init . Comme je l'ai dit, vars () ne retournera pas les attributs de classe, vous devez utiliser dir () ou inspect.getmembers ()
user2682863
2

Cela peut être fait sans inspecter, je suppose.

Suivez le cours suivant:

 class Test:
   a = 1
   b = 2

   def __init__(self):
     self.c = 42

   @staticmethod
   def toto():
     return "toto"

   def test(self):
     return "test"

En regardant les membres avec leurs types:

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

... donne:

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

Donc, pour ne sortir que les variables, il vous suffit de filtrer les résultats par type et les noms ne commençant pas par «__». Par exemple

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

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

C'est tout.

Remarque: si vous utilisez Python 3, convertissez les itérateurs en listes.

Si vous voulez une manière plus robuste de le faire, utilisez inspect .

carmellose
la source
2

Python 2 & 3, sans importations, filtrage des objets par leur adresse

Solutions en bref:

Renvoie dict {nom_attribut: valeur_attribut} , objets filtrés. c'est à 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)}

Liste de retour [nom_attribut] , objets filtrés. c'est à dire['a', 'b', 'c', 'd']

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

Liste de retour [valeur_attribut] , objets filtrés. c'est à 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)]

Ne pas filtrer les objets

Suppression de la ifcondition. Revenir{'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()}

Solution en long

Tant que l'implémentation par défaut de __repr__n'est pas remplacée, l' ifinstruction retournera Truesi la représentation hexadécimale de l'emplacement en mémoire de valest dans la __repr__chaîne de retour.

Concernant l'implémentation par défaut de, __repr__vous pourriez trouver utile cette réponse . En bref:

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

Qui renvoie une chaîne comme:

<__main__.Bar object at 0x7f3373be5998>

L'emplacement en mémoire de chaque élément est obtenu via la id()méthode.

Python Docs dit à propos de id ():

Renvoie «l'identité» d'un objet. Il s'agit d'un entier garanti unique et constant pour cet objet pendant sa durée de vie. Deux objets dont les durées de vie ne se chevauchent pas peuvent avoir la même valeur id ().

Détail de l'implémentation CPython: il s'agit de l'adresse de l'objet en mémoire.


Essayez par vous-même

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

Production:

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

Remarque :

  • Testé avec Python 2.7.13et Python3.5.3

  • En Python 2.x .iteritems()est préférable à.items()

Marco DG
la source
1

J'ai récemment eu besoin de trouver quelque chose de similaire à cette question, donc je voulais publier des informations de base qui pourraient être utiles à d'autres personnes confrontées à la même chose à l'avenir.

Voici comment cela fonctionne en Python (à partir de https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy ):

MyClassest un objet de classe, MyClass()est une instance de l'objet de classe. Une instance __dict__contient uniquement des attributs et des méthodes spécifiques à cette instance (par exemple self.somethings). Si un attribut ou une méthode fait partie d'une classe, il est dans la classe __dict__. Lorsque vous le faites MyClass().__dict__, une instance de MyClassest créée sans attribut ni méthode en plus des attributs de classe, donc le vide__dict__

Donc, si vous dites print(MyClass().b), Python vérifie d'abord le dict de la nouvelle instance MyClass().__dict__['b']et ne parvient pas à le trouver b. Il vérifie ensuite la classe MyClass.__dict__['b']et trouve b.

C'est pourquoi vous avez besoin du inspectmodule pour émuler ce même processus de recherche.

Scott Howard
la source
2
Scott - Un commentaire posté en tant que réponse doit être supprimé, sinon nous serions noyés dedans. Cependant, une réponse partielle ou un «coup de pouce utile» vers une solution reste une réponse . Vous verrez comment j'ai reformulé votre message; j'espère que j'ai conservé votre intention. Sinon, vous pouvez le modifier davantage en forme. À votre santé!
Mogsdad
1

Vous pouvez utiliser dir()dans une compréhension de liste pour obtenir les noms d'attributs:

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

Utilisation getattr() pour obtenir les attributs eux-mêmes:

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

Ma solution pour obtenir tous les attributs (et non les méthodes) d'une classe (si la classe a une docstring correctement écrite avec les attributs clairement énoncés):

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

Cette pièce cls.__dict__['__doc__']extrait la docstring de la classe.

Henry On
la source
1

Pourquoi avez-vous besoin de lister les attributs? Il semble que sémantiquement votre classe est une collection. Dans ce cas, je recommande d'utiliser enum:

import enum

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

Listez vos attributs? Rien de plus simple que ça:

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

Si vous voulez "obtenir" un attribut, il y a une réponse très simple , qui devrait être évidente: 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>

Cela fonctionne dandy à la fois en Python 2.7 et Python 3.x.

Si vous voulez une liste de ces éléments, vous devrez toujours utiliser inspect.

Fralau
la source
1
Cette réponse est-elle trop simple et trop correcte pour mériter des points, et devrait même mériter de mauvais points? Il semble que l'économie et la simplicité ne rapportent plus de nos jours.
fralau
0

deux fonctions:

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

utilisation:

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

>>> m = MyClass()
>>> get_class_attr_val(m)
{'a': '12', 'b': '34'}
écharpe rouge
la source
0

Voici ce que je veux.

Données de 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']
Carson
la source
-2

Je sais que c'était il y a trois ans, mais pour ceux qui viendront se poser cette question à l'avenir, pour moi:

class_name.attribute 

fonctionne très bien.

Jim Jam
la source
3
sauf lorsque vous obtenez une AttributeError.
rady
Vous ne savez pas toujours ce qui attributeest à l'avance.
Matt Luongo
-3

Vous pouvez utiliser MyClass.__attrs__. Il donne juste tous les attributs de cette classe. Rien de plus.

Jimxliu
la source
AttributeError: l'objet de type 'X' n'a pas d'attribut ' attrs '
Ramazan Polat