web-dev-qa-db-fra.com

Obtenir les attributs d'une classe

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

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

  def myfunc(self):
    return self.a

utiliser MyClass.__dict__ me donne une liste d'attributs et de fonctions, et même des fonctions telles que __module__ et __doc__. Alors que 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: a et b

60
Mohamed Khamis

Essayez le module inspect . getmembers et les divers tests devraient être utiles.

MODIFIER:

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 les attributs spéciaux m'énervent - ceux-ci peuvent être traités de différentes manières, la plus simple étant de filtrer par 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 les plus complexes pouvant inclure des vérifications spéciales du nom d'attribut ou même des métaclasses;)

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

properties = props(MyClass)
23
Doug

myfuncis un attribut de MyClass. C'est comme ça qu'on le trouve quand on court:

myinstance = MyClass()
myinstance.myfunc()

Il cherche un attribut sur myinstance nommé myfunc, n'en trouve pas, voit que myinstance est une instance de MyClass et le recherche à cet endroit.

La liste d'attributs complete pour MyClass est donc:

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

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

Si vous ne voulez que des attributs particuliers, vous devrez filtrer cette liste à l'aide de certains critères, car __doc__, __module__ et myfunc ne sont aucunement spéciaux, ils sont exactement comme les attributs a et b.

Je n'ai jamais utilisé le module inspect évoqué par Matt et Borealid, mais d'après un bref lien, il semble contenir des tests pour vous aider à faire cela, mais vous devrez écrire votre propre fonction de prédicat, car il semble que ce que vous voulez. est à peu près les attributs qui not ne réussissent pas le test isroutine et ne commencent pas et se terminent par deux traits de soulignement.

Remarque: en utilisant class MyClass(): dans Python 2.7, vous utilisez des classes obsolètes et obsolètes. À moins que vous ne le fassiez délibérément pour assurer la compatibilité avec des bibliothèques extrêmement anciennes, définissez plutôt votre classe comme class MyClass(object):. En Python 3, il n'y a pas de classes "à l'ancienne", et ce comportement est le comportement par défaut. Cependant, en utilisant les classes newstyle, vous obtiendrez un lot attributs plus automatiquement définis:

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

MyClass().__class__.__dict__

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

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

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

Pour obtenir le dictionnaire complet pour 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('__'))]
2
JD Graham

Ma solution pour obtenir tous les attributs (pas les méthodes) d'une classe (si la classe a une docstring correctement écrite qui a 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.

2
Henry On

Je ne sais pas si quelque chose de similaire a été fait à ce jour ou non, mais j'ai créé une fonction de recherche d'attribut Nice avec vars (). vars () crée un dictionnaire des attributs d'une classe que vous traversez.

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 de texte assez massive en Python, ma classe de joueurs compte jusqu'à présent plus de 100 attributs. J'utilise ceci pour rechercher des attributs spécifiques que je dois voir.

2
Corey Bailey

Cela peut être fait sans inspecter, je suppose.

Prenez 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 produire que les variables, il vous suffit de filtrer les résultats par type et par 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 un moyen plus robuste de le faire, utilisez inspect .

1
carmellose

J'ai récemment eu besoin de trouver quelque chose de similaire à cette question, alors je voulais poster des informations de fond qui pourraient être utiles aux personnes confrontées aux mêmes problèmes à l'avenir.

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

MyClass est un objet de classe, MyClass() est une instance de l'objet de classe. __dict__ d'une instance ne contient que 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 se trouve dans le __dict__ de la classe. Lorsque vous faites MyClass().__dict__, une instance de MyClass est créée sans aucun attribut ni méthode à part les attributs de classe, ainsi le __dict__ vide

Donc, si vous dites print(MyClass().b), Python commence par vérifier la fonction dict MyClass().__dict__['b'] de la nouvelle instance et ne trouve pas b. Il vérifie ensuite la classe MyClass.__dict__['b'] et trouve b.

C'est pourquoi vous avez besoin du module inspect pour émuler le même processus de recherche.

1
Scott Howard

Obtenir uniquement les attributs instance est facile.
Mais obtenir aussi les attributs class sans les fonctions est un peu plus compliqué.

Attributs d'instance uniquement

Si vous devez seulement lister les attributs 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

Instance et attributs de classe

Pour obtenir également les attributs class sans les fonctions, l'astuce consiste à utiliser callable()

Mais méthodes statiques sont pas toujours callable !

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 MyClass.__dict__.keys():
       if attribute[:2] != '__':
         value = getattr(MyClass, 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 @staticmethod
mais pas dans cet stupide et simple exemple.

Résultat pour python2

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

Même résultat pour python3

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

Vous pouvez utiliser dir() dans un compréhension de la liste pour obtenir les noms d'attribut:

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

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

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

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

0
jimxliu

Python 2 & 3, sans importations, filtrant les objets par leur adresse

Solutions en bref:

Retourner dict {nom_attribut: valeur_attribut} , objets filtrés. i.e {'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)}

Retour list [noms_attributs] , 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)]

Retour list [valeurs_attributs] , objets filtrés. i.e [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 condition if. Retourne {'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 à long

Tant que l'implémentation par défaut de __repr__ n'est pas remplacée , l'instruction if renverra True si l'hexadécimal La représentation de l'emplacement en mémoire de val est dans la chaîne de retour __repr__.

En ce qui concerne 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)))

Ce qui retourne une chaîne comme:

<__main__.Bar object at 0x7f3373be5998>

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

Python Docs dit à propos de id ():

Renvoie "l'identité" d'un objet. C'est un entier qui est garanti d'être unique et constant pour cet objet tout au long de sa vie. Deux objets dont la durée de vie ne se chevauchent pas peuvent avoir la même valeur id ().

Détail d'implémentation CPython: C'est 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())

Sortie:

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

Remarque :

  • Testé avec Python 2.7.13 et Python 3.5.3

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

0
Marco D.G.

Il y a une réponse très simple , cela devrait être évident: 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 bien à la fois en Python 2.7 et en Python 3.x.

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

0
fralau

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'}
0
redscarf