Iteracja atrybutów obiektów w Pythonie

Mam obiekt Pythona z kilkoma atrybutami i metodami. Chcę iterację nad atrybutami obiektu.

class my_python_obj(object):
    attr1='a'
    attr2='b'
    attr3='c'

    def method1(self, etc, etc):
        #Statements

Chcę wygenerować słownik zawierający wszystkie atrybuty obiektów i ich bieżące wartości, ale chcę to zrobić w sposób dynamiczny(więc jeśli później dodam kolejny atrybut, nie muszę pamiętać o aktualizacji mojej funkcji).

W php zmienne mogą być używane jako klucze, ale obiekty w Pythonie są nieuscriptable i jeśli używam do tego notacji kropki tworzy nowy atrybut o nazwie My var, co nie jest moim zamiarem.

Żeby było jaśniej:

def to_dict(self):
    '''this is what I already have'''
    d={}
    d["attr1"]= self.attr1
    d["attr2"]= self.attr2
    d["attr3"]= self.attr3
    return d

·

def to_dict(self):
    '''this is what I want to do'''
    d={}
    for v in my_python_obj.attributes:
        d[v] = self.v
    return d

Update: Z atrybutami mam na myśli tylko zmienne tego obiektu, a nie metody.

Author: Eric Leschinski, 2012-07-24

7 answers

Zakładając, że masz klasę taką jak

>>> class Cls(object):
...     foo = 1
...     bar = 'hello'
...     def func(self):
...         return 'call me'
...
>>> obj = Cls()

Wywołanie dir na obiekcie zwraca wszystkie atrybuty tego obiektu, w tym specjalne atrybuty Pythona. Chociaż niektóre atrybuty obiektów są wywoływalne, takie jak metody.

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo', 'func']

Zawsze możesz odfiltrować specjalne metody za pomocą rozumienia listy.

>>> [a for a in dir(obj) if not a.startswith('__')]
['bar', 'foo', 'func']

Lub jeśli wolisz mapy / filtry.

>>> filter(lambda a: not a.startswith('__'), dir(obj))
['bar', 'foo', 'func']

Jeśli chcesz odfiltrować metody, możesz użyć wbudowanego callable jako szach.

>>> [a for a in dir(obj) if not a.startswith('__') and not callable(getattr(obj,a))]
['bar', 'foo']

Możesz również sprawdzić różnicę między klasą a jej rodzicem za pomocą.

>>> set(dir(Cls)) - set(dir(object))
set(['__module__', 'bar', 'func', '__dict__', 'foo', '__weakref__'])
 154
Author: Meitham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-09 10:32:26

Ogólnie umieść metodę __iter__ w swojej klasie i powtarzaj przez atrybuty obiektu lub umieść tę klasę mixin w swojej klasie.

class IterMixin(object):
    def __iter__(self):
        for attr, value in self.__dict__.iteritems():
            yield attr, value

Twoja klasa:

>>> class YourClass(IterMixin): pass
...
>>> yc = YourClass()
>>> yc.one = range(15)
>>> yc.two = 'test'
>>> dict(yc)
{'one': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 'two': 'test'}
 39
Author: SmartElectron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-26 10:29:39

Obiekty w Pythonie przechowują swoje atrybuty (w tym funkcje) w dict o nazwie __dict__. Możesz (ale generalnie nie powinieneś) użyć tego, aby uzyskać bezpośredni dostęp do atrybutów. Jeśli potrzebujesz tylko listy, możesz również wywołać dir(obj), która zwraca iterowalną z wszystkimi nazwami atrybutów, które możesz następnie przekazać do getattr.

Jednak, konieczność robienia czegokolwiek z nazwami zmiennych jest zwykle złym projektem. Dlaczego nie trzymać ich w kolekcji?

class Foo(object):
    def __init__(self, **values):
        self.special_values = values

Można następnie iterację nad kluczami z for key in obj.special_values:

 2
Author: Daenyth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 18:56:16
class someclass:
        x=1
        y=2
        z=3
        def __init__(self):
           self.current_idx = 0
           self.items = ["x","y","z"]
        def next(self):
            if self.current_idx < len(self.items):
                self.current_idx += 1
                k = self.items[self.current_idx-1]
                return (k,getattr(self,k))
            else:
                raise StopIteration
        def __iter__(self):
           return self

Więc nazwij to jako iterable

s=someclass()
for k,v in s:
    print k,"=",v
 1
Author: Joran Beasley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 19:05:21

Dla Pythona 3.6

class SomeClass:

    def attr_list(self, should_print=False):

        items = self.__dict__.items()
        if should_print:
            [print(f"attribute: {k}    value: {v}") for k, v in items]

        return items
 1
Author: Rubber Duck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-09 16:23:39

Dla Pythona 3.6

class SomeClass:

    def attr_list1(self, should_print=False):

        for k in self.__dict__.keys():
            v = self.__dict__.__getitem__(k)
            if should_print:
                print(f"attr: {k}    value: {v}")

    def attr_list(self, should_print=False):

        b = [(k, v) for k, v in self.__dict__.items()]
        if should_print:
            [print(f"attr: {a[0]}    value: {a[1]}") for a in b]
        return b
 0
Author: Rubber Duck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-09 15:54:50

Poprawna odpowiedź jest taka, że nie powinieneś. jeśli chcesz tego typu rzeczy albo po prostu użyj dict, albo będziesz musiał wyraźnie dodać atrybuty do jakiegoś kontenera. Możesz to zautomatyzować, ucząc się o dekoratorach.

W szczególności, przy okazji, method1 w twoim przykładzie jest tak samo dobry z atrybutu.

 -1
Author: Julian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 18:55:52